Merge branch 'feature/pack' into feature/promote

master
ELF 5 years ago
commit eb15fc7dfc

@ -19,6 +19,8 @@ use BaiduBce\Services\Bos\BosOptions;
use BaiduBce\Auth\SignOptions;
use BaiduBce\Log\LogFactory;
use Think\Think;
use Base\Service\GameSourceService;
use Base\Tool\Printer;
/**
* 后台首页控制器
@ -48,10 +50,10 @@ class AutoPackController extends Think
}
$this->checkPackageStatus();
$this->app_package();//app渠道自动打包
// $this->app_package();//app渠道自动打包
$this->channel();
$this->launch_package(false);
$this->leak_detection();
// $this->launch_package(false);
// $this->leak_detection();
}
/**
@ -74,12 +76,10 @@ class AutoPackController extends Think
->where('1=1')
->group('l.id')
->select();
if(!empty($launchGameIds)) {
if(!empty($launchGameIds)) {
$ids = array_column($launchGameIds, 'id');
$launchModel->where(['id'=>['in',$ids]])->setField('launch_packge', 2);
}
$launchIds = $launchModel
@ -92,18 +92,18 @@ class AutoPackController extends Think
->group('apply_id')
->select();
$launchId='';
$launchId = [];
foreach ($launchIds as $launchValue) {
foreach ($launchAllIds as $launchAllValue) {
if($launchValue['apply_id'] == $launchAllValue['apply_id']
&& $launchValue['count'] == $launchAllValue['count']) {
$launchId[]=$launchValue['apply_id'];
$launchId[] = $launchValue['apply_id'];
}
}
}
if(!empty($launchId)) {
$applyModel->where(['id'=>['in',$launchId]])->setField('enable_status', 1);
$applyModel->where(['id'=> ['in',$launchId]])->setField('enable_status', 1);
}
}
@ -292,14 +292,6 @@ class AutoPackController extends Think
/**
* 渠道游戏打包
*
* 针对每个申请游戏检测是否存在投放申请:有则更改投放申请状态,使其进入准备打包状态;无则进行渠道游戏打包
*
* @author: 鹿文学[lwx]<fyj301415926@126.com>
* @since: 2019\4\17 0017 14:27
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\ModelNotFoundException
* @throws \think\exception\DbException
*/
private function channel() {
$zip = new \ZipArchive();
@ -311,140 +303,44 @@ class AutoPackController extends Think
$gameSourceModel = M('Game_source', 'tab_');
$launchModel = M('apply_launch', 'tab_');
$apply_data = $applyModel
-> field('id,game_id,game_name,promote_id,promote_account,sdk_version')
-> where($map)
-> order('bale_sort desc,id desc')
-> limit(200)
-> select();
if (!empty($apply_data)) {
foreach ($apply_data as $key => $value) {
$game_so = $gameSourceModel
-> field('id,file_name,source_version')
-> where(['game_id' => $value['game_id']])
-> find();
if (!file_exists(get_game_source_file_url($value['game_id'])) || null == $game_so) {
$applyModel -> where(['id' => $value['id']]) -> setField('enable_status', -1);
continue;
}
/* 检测是否存在投放申请,存在则更改投放申请信息,否则进行渠道打包 */
$launchCount = $launchModel->where(['apply_id'=>$value['id']])->count();
if($launchCount>0) {
$applyModel->where(['id' => $value['id']]) -> setField('enable_status', 3);
$launchModel->where(['apply_id'=>$value['id'], 'launch_packge'=>['in',[0,2,3]]])->save(['launch_packge'=>2,'launch_down_url'=>'','launch_plist_url'=>'']);
$applys = $applyModel
->field('id,game_id,game_name,promote_id,promote_account,sdk_version')
->where($map)
->order('bale_sort desc,id desc')
->limit(200)
->select();
if (count($applys) == 0) {
Printer::export('无等待队列', true);
}
$applyIds = array_column($applys, 'id');
$gameIds = array_unique(array_column($applys, 'game_id'));
$games = M('game', 'tab_')->field(['id', 'game_appid'])->where(['id' => ['in', $gameIds]])->select();
$games = index_by_column('id', $games);
$launchCountList = $launchModel->field(['apply_id', 'count(*) count'])->where(['apply_id' => ['in', $applyIds]])->group('apply_id')->select();
$launchCountList = index_by_column('apply_id', $launchCountList);
$gameSources = M('game_source', 'tab_')->field(['id', 'file_name', 'source_version', 'file_type', 'bao_name', 'game_id'])->where(['game_id' => ['in', $gameIds]])->select();
$gameSources = index_by_column('game_id', $gameSources);
$gameSourceService = new GameSourceService();
if (!empty($applys)) {
foreach ($applys as $apply) {
$gameSource = $gameSources[$apply['game_id']] ?? null;
$game = $games[$apply['game_id']];
$launchCount = $launchCountList[$apply['id']] ?? 0;
$result = $gameSourceService->channelPack($gameSource, $game, $apply, $launchCount);
$message = '游戏['. $apply['game_id'] . '],渠道['. $apply['promote_id'] . ']打包,';
if ($result['status']) {
$message = 'SUCCESS ----- ' . $message . $result['message'];
} else {
$applyModel -> where(['id' => $value['id']]) -> setField('enable_status', 3);
if ($value['sdk_version'] == 1) {
$str_ver = ".apk";
$file_name = "GamePack";
$url_ver = "META-INF/mch.properties";
} else {
$zip_open_game_source = zip_open(get_game_source_file_url($value['game_id']));
if ($zip_open_game_source) {
while ($zip_entry = zip_read($zip_open_game_source)) {
if (preg_match("/.app/", zip_entry_name($zip_entry))) {
$ios_app = substr(zip_entry_name($zip_entry), 8) . "<br/>";
}
$new_ios_1 = explode("/", $ios_app);
}
zip_close($zip_open_game_source);
}
$str_ver = ".ipa";
$file_name = "IosGamePack";
$url_ver = "Payload/" . $new_ios_1[0] . "/_CodeSignature/TXChannel";
}
$new_name = "game_package" . $value['game_id'] . "-" . $value['promote_id'] . $str_ver;
$to = "./Uploads/" . $file_name . "/" . $new_name;
$localPath = ROOTTT . ltrim($to, './');
copy(get_game_source_file_url($value['game_id']), $localPath);
$zip_res = $zip -> open(ROOTTT . ltrim($to, './'), \ZipArchive::CREATE);
if ($zip_res == true) {
#打包数据
$pack_data = array(
"game_id" => $value["game_id"],
"game_name" => $value['game_name'],
"game_appid" => get_game_appid($value["game_id"], "id"),
"promote_id" => $value['promote_id'],
"promote_account" => $value['promote_account'],
"source_version" => $game_so['source_version'],
);
$zip -> addFromString($url_ver, json_encode($pack_data));
$zip -> close();
$newname = "game_package" . $value["game_id"] . "-" . $value['promote_id'] . $str_ver;
if (get_tool_status("oss_storage") == 1) {
if (C("oss_storage.is_cname")) {
$to = "http://" . C("oss_storage.domain") . "/" . str_replace("IosGamePack", "GamePack", $file_name) . "/" . $newname;
} else {
$to = "https://" . C("oss_storage.bucket") . "." . C("oss_storage.domain") . "/" . str_replace("IosGamePack", "GamePack", $file_name) . "/" . $newname;
$to = str_replace('-internal', '', $to);
}
if (!empty(C('oss_storage.bd_domain')) && strlen(C('oss_storage.bd_domain')) > 5) {
$to = C('oss_storage.bd_domain') . "/GamePack/" . $newname;
}
$new_to = ROOTTT . "Uploads/" . $file_name . "/" . $newname;
$updata['savename'] = $newname;
$updata['path'] = $new_to;
$this -> upload_game_pak_oss($updata);
if (file_exists($new_to)) {
@unlink($new_to);
}
} elseif (get_tool_status("qiniu_storage") == 1) {
$this -> dleteQiNiuFile($newname);
$url = $this -> upQiNiuFile($newname, ROOTTT . ltrim($to, './'));
if (empty($url)) {
$this -> error('七牛错误,请检查七牛配置,并确保七牛空间权限正确!');
}
@unlink(ROOTTT . ltrim($to, './'));
$to = "http://" . $url;
} elseif (get_tool_status("cos_storage") == 1) {
$new_to = ROOTTT . "Uploads/" . $file_name . "/" . $newname;
$updata['path'] = $new_to;
$updata['savename'] = 'GamePack/'. $newname;
$to = $this->upload_cos($updata);
} elseif (get_tool_status("bos_storage") == 1) {
$newname = "game_package" . $value["game_id"] . "-" . $value['promote_id'] . $str_ver;
$to = "http://" . C("bos_storage.bucket") . "." . C("bos_storage.domain") . "/" . $file_name . "/" . $newname;
$to = str_replace('-internal', '', $to);
$new_to = ROOTTT . "Uploads/" . $file_name . "/" . $newname;
$updata['savename'] = $newname;
$updata['path'] = $new_to;
$this -> upload_bos($updata);
}
$promote = array('game_id' => $value['game_id'], 'promote_id' => $value['promote_id']);
$plist_url = '';
if ($value['sdk_version'] == 2) {
$plist_url = $this -> create_plist($promote['game_id'], $promote['promote_id'], get_payload_name($value['game_id']), $to);
}
$jieguo = $this -> updateinfo($value['id'], $to, $promote, $plist_url);
}
$message = 'ERROR ----- ' . $message . $result['message'];
}
Printer::export($message);
}
}
}
@ -456,21 +352,26 @@ class AutoPackController extends Think
$map['a.status'] = 1;
$map['a.enable_status'] = 1;
$map['al.launch_packge'] = array('in', [0, 2, 3]);
$apply = $applymodel -> alias('a')
-> field('a.id,a.game_id,a.game_name,a.promote_account,a.pack_url,a.plist_url,a.promote_id,a.sdk_version,a.enable_status')
-> join('tab_apply_launch as al on (al.apply_id=a.id) ')
-> where($map) -> limit(3) -> group('a.id') -> select();
$apply = $applymodel->alias('a')
->field('a.id,a.game_id,a.game_name,a.promote_account,a.pack_url,a.plist_url,a.promote_id,a.sdk_version,a.enable_status')
->join('tab_apply_launch as al on (al.apply_id=a.id) ')
->where($map)
->limit(300)
->group('a.id')
->select();
foreach ($apply as $key => $value) {
$game_so = $game_somodel -> field('id,file_name,source_version') -> where(['game_id' => $value['game_id']]) -> find();
if (!file_exists(get_game_source_file_url($value['game_id'])) || null == $game_so) {
$gameSource = $gameSourceModel
->field('id,file_name,source_version,file_type')
->where(['game_id' => $value['game_id']])
->find();
$gameSourceUrl = $gameSourceService->getGameSourceUrl($gameSource);
if ($gameSource || !file_exists($gameSourceUrl)) {
M('apply', 'tab_') -> where(['id' => $value['id']]) -> setField('enable_status', -1);
$launchmodel -> where(['apply_id' => $value['id']]) -> setField('launch_packge', 0);
continue;
}
$url = get_game_source_file_url($value['game_id']);
if (!empty($url) && is_file($url)) {
if (!empty($gameSourceUrl) && is_file($gameSourceUrl)) {
$launch = $launchmodel -> where(['apply_id' => $value['id'], 'launch_packge' => array('in', [0, 2, 3])]) -> select();
$zip = new \ZipArchive();
@ -478,19 +379,19 @@ class AutoPackController extends Think
if ($value['sdk_version'] == 1) {
if (!empty($v['game_source_filename'])) {
if ($v['game_source_filename'] == $game_so['file_name']) {
if ($v['game_source_filename'] == $gameSource['file_name']) {
if (!empty($v['launch_down_url'])) {
$launchmodel -> where(['id' => $v['id']]) -> setField(['launch_packge' => 1]);
continue;
}
}
}
$str_ver = ".apk";
$str_ver = '.apk';
$file_name = "GamePack";
$url_ver = "META-INF/mch.properties";
} else {
if (!empty($v['game_source_filename'])) {
if ($v['game_source_filename'] == $game_so['file_name']) {
if ($v['game_source_filename'] == $gameSource['file_name']) {
if (!empty($v['launch_plist_url'])) {
if (!empty($v['launch_down_url'])) {
$launchmodel -> where(['id' => $v['id']]) -> setField(['launch_packge' => 1]);
@ -499,7 +400,7 @@ class AutoPackController extends Think
}
}
}
$zip_open_game_source = zip_open($url);
$zip_open_game_source = zip_open($gameSourceUrl);
if ($zip_open_game_source) {
while ($zip_entry = zip_read($zip_open_game_source)) {
if (preg_match("/.app/", zip_entry_name($zip_entry))) {
@ -516,7 +417,7 @@ class AutoPackController extends Think
}
$new_name = "game_package" . $value['game_id'] . "-" . $value['promote_id'] . "-" . $v['platform_id'] . "-" . $v['position'] . $str_ver;
$to = "./Uploads/" . $file_name . "/" . $new_name;
copy($url, ROOTTT . ltrim($to, './'));
copy($gameSourceUrl, ROOTTT . ltrim($to, './'));
$zip_res = $zip -> open(ROOTTT . ltrim($to, './'), \ZipArchive::CREATE);
if ($zip_res == true) {
#打包数据
@ -526,7 +427,7 @@ class AutoPackController extends Think
"game_appid" => get_game_appid($value["game_id"], "id"),
"promote_id" => $value['promote_id'],
"promote_account" => $value['promote_account'],
"source_version" => $game_so['source_version'],
"source_version" => $gameSource['source_version'],
'launch_id' => $v['platform_id'],
'position' => $v['position'],
);
@ -595,7 +496,7 @@ class AutoPackController extends Think
if ($value['sdk_version'] == 2) {
$plist_url = $this -> create_plist($promote['game_id'], $promote['promote_id'], get_payload_name($value['game_id']), $to, $v['platform_id'], $v['position']);
}
$jg = $this -> update_launch_info(['platform_id' => $v['platform_id'], 'apply_id' => $value['id'], 'position'=>$v['position']], $to, $plist_url, $game_so['file_name']);
$jg = $this -> update_launch_info(['platform_id' => $v['platform_id'], 'apply_id' => $value['id'], 'position'=>$v['position']], $to, $plist_url, $gameSource['file_name']);
}
@ -643,7 +544,7 @@ class AutoPackController extends Think
{
$map['status'] = 1;
$map['enable_status'] = 2;
$arr_id = M('app_apply', 'tab_') -> field('id') -> where($map) -> order('id desc') -> limit(3) -> select();
$arr_id = M('app_apply', 'tab_')->field('id')->where($map)->order('id desc')->limit(300)->select();
if (empty($arr_id)) {
return false;
}
@ -722,8 +623,6 @@ class AutoPackController extends Think
$apply_data['dow_url'] = $to;
$apply_data['enable_status'] = 1;
M('app_apply', 'tab_') -> save($apply_data);
}
}
@ -736,7 +635,7 @@ class AutoPackController extends Think
*/
public function updateinfo($id, $pack_url, $promote, $plist_url)
{
$model = M('Apply', "tab_");
$data = [];
$data['id'] = $id;
$data['pack_url'] = $pack_url;
$data['dow_url'] = '/index.php?s=/Home/Down/down_file/game_id/' . $promote['game_id'] . '/promote_id/' . $promote['promote_id'];
@ -745,7 +644,7 @@ class AutoPackController extends Think
$data['dispose_id'] = UID;
$data['dispose_time'] = NOW_TIME;
$data['plist_url'] = $plist_url;
$res = $model -> save($data);
$res = M('Apply', 'tab_')->save($data);
return $res;
}

@ -0,0 +1,27 @@
<?php
/**
* 定时自动完成
*/
namespace Admin\Controller;
use Admin\Model\SpendModel;
use Think\Think;
use Org\RedisSDK\Redis;
class AutoController extends Think {
protected function _initialize()
{
C(api('Config/lists'));
}
public function modifyUserRole()
{
M('user_play_info', 'tab_')->select(['game_id', 'server_id', 'role_id'])->group('game_id, server_id, role_id')->having('count(*)>1')->select();
}
public function modifyRecharge()
{
}
}

@ -54,8 +54,8 @@ class GameSourceController extends ThinkController
$extend = substr($_POST['file_name'], strlen($_POST['file_name']) - 3, 3);
if ($_POST['file_type'] == 1 && $extend != 'apk') {
$this -> error('游戏原包格式不正确!');
} else if ($_POST['file_type'] == 2 && $extend != 'ipa' || $_POST['file_type'] == 2 && empty($_POST['bao_name'])) {
$this -> error('游戏原包格式不正确/包名不能为空');
} else if ($_POST['file_type'] == 2 && $extend != 'ipa') {
$this -> error('游戏原包格式不正确');
}
}
$map['game_id'] = $_POST['game_id'];
@ -136,17 +136,17 @@ class GameSourceController extends ThinkController
$extend = substr($_POST['file_name'], strlen($str) - 3, 3);
if ($_POST['file_type'] == 1 && $extend != 'apk') {
$this -> error('游戏原包格式不正确!');
} else if ($_POST['file_type'] == 2 && $extend != 'ipa' || $_POST['file_type'] == 2 && empty($_POST['bao_name'])) {
$this -> error('游戏原包格式不正确/包名不能为空');
} else if ($_POST['file_type'] == 2 && $extend != 'ipa') {
$this -> error('游戏原包格式不正确');
}
}
$map['file_type'] = $_POST['file_type'];
$d = D('Game_source') -> where($map) -> find();
$source = A('Source', 'Event');
if (empty($d)) {
$source -> add_source();
$source->add_source();
} else {
$source -> update_source($d['id'], $d['file_name']);
$source->update_source($d['id'], $d['file_name']);
}
} else {
$d = M('GameSource', "tab_") -> where($map) -> find();

@ -9,6 +9,9 @@
namespace Admin\Event;
use Think\Controller;
use Base\Service\GameSourceService;
use Base\Tool\PlistParser;
use Base\Tool\ApkParser;
/**
* 后台首页控制器
@ -20,38 +23,52 @@ class SourceEvent extends Controller
public function add_source()
{
$model = D('Game_source');
$plist = A("Plist");
$data = $_REQUEST;
$data['file_size'] = round($data['file_size'] / pow(1024, 2), 2) . "MB";
/*if ($data['file_type'] == 2) {
if (preg_match("/.ipa/", $data['file_name'])) {
//copy($data['file_url'] . "/" . $data['file_name'], "./Uploads/Ios/original/" . $data['file_name']);
//@unlink($data['file_url'] . "/" . $data['file_name']);
//$data['file_url'] = "./Uploads/Ios/original";
}
}*/
$data['file_url'] = $data['file_url'] . "/" . $data['file_name'];
$data['sdk_version'] = $data['file_type'];
$data['op_id'] = UID;
$data['version'] = $_POST['version'];
$data['op_account'] = session("user_auth.username");
$data['create_time'] = NOW_TIME;
$remark = str_replace(array("\r\n", "\r", "\n"), "@@@", $_POST['remark']);
$data['remark'] = json_encode(explode('@@@', $remark));
if ($data['file_type'] == 2) {
$data['plist_url'] = "./Uploads/SourcePlist/" . $data['game_id'] . ".Plist";
$data['org_plist_url'] = "./Uploads/OrgSourcePlist/" . $data['game_id'] . ".Plist";
$plist -> create_plist($data['game_id'], 0, $data['bao_name'], $data['file_url']);
$data['source_version'] = 0;
$version = '';
$packageName = '';
if ($data['file_type'] == 1) {
$parser = new ApkParser();
$parser->open($data['file_url']);
$version = $parser->getVersionName();
$packageName = $parser->getPackage();
} elseif($data['file_type'] == 2) {
$parser = new PlistParser();
$parser->openFromIpa($data['file_url'], PlistParser::PREG_INFO_PLIST);
$result = $parser->getResult();
$version = $result['CFBundleShortVersionString'];
$packageName = $result['CFBundleIdentifier'];
}
$res = $model -> add($data);
if ($res) {
$data['version'] = $version;
$data['bao_name'] = $packageName;
$game = M('game', 'tab_')->where(['id' => $data['game_id']])->find();
$gameSourceService = new GameSourceService();
$result = $gameSourceService->sourcePack($data, $game);
var_dump($result);
if (!$result['status']) {
var_dump($result);die();
$this->error('添加失败' , U('GameSource/lists', ['type' => $data['type']]));
}
$data['plist_url'] = $result['data']['plistUrl'];
$data['org_plist_url'] = $result['data']['orgPlistUrl'];
$data['file_url'] = $result['data']['fileUrl'];
$data['original_url'] = $result['data']['originalUrl'];
if (M('game_source', 'tab_')->add($data)) {
//原包打包
$this -> soure_pack($data['game_id'], $data['file_url'], $data['file_type'], 1, $data['bao_name']);
$this -> update_game_size($data);
$this -> success('添加成功', U('GameSource/lists', array('type' => $data['type'])));
$this->update_game_size($data);
var_dump('success');die();
$this->success('添加成功', U('GameSource/lists', ['type' => $data['type']]));
} else {
$this -> error('添加失败', U('GameSource/lists', array('type' => $data['type'])));
var_dump('error');die();
$this->error('添加失败', U('GameSource/lists', ['type' => $data['type']]));
}
}
@ -65,23 +82,8 @@ class SourceEvent extends Controller
$plist = A("Plist");
$data = $_REQUEST;
$url = $data['file_url'];
$urll = $data['file_url2'];
/*if ($data['file_type'] == 2) {
if (preg_match("/.ipa/", $data['file_name']) && ($data['file_name'] != $file_name)) {
//copy($data['file_url'] . "/" . $data['file_name'], "./Uploads/Ios/original/" . $data['file_name']);
//@unlink($data['file_url'] . "/" . $data['file_name']);
//$data['file_url'] = "./Uploads/Ios/original";
//$url = "./Uploads/Ios/original";
}
}*/
$urll = $data['file_url2'];
$data['file_size'] = strpos($data['file_size'], 'MB') ? $data['file_size'] : round($data['file_size'] / pow(1024, 2), 2) . "MB";
/*$extend = substr($data['file_url'], strlen($str) - 3, 3);
if ($extend != "apk" && $extend != "ipa") {
$data['file_url'] = $data['file_url'] . "/" . $data['file_name'];
}*/
$data['file_url'] = $data['file_url'] . "/" . $data['file_name'];
$data['id'] = $id;
$data['op_id'] = UID;
@ -89,69 +91,72 @@ class SourceEvent extends Controller
$data['create_time'] = NOW_TIME;
$remark = str_replace(array("\r\n", "\r", "\n"), "@@@", $_POST['remark']);
$data['remark'] = json_encode(explode('@@@', $remark));
$game_source = $model -> where(array('id' => $id)) -> find();
$game_source = $model->where(['id' => $id])->find();
if($game_source['file_name'] == $data['file_name']) {
if ($from == "dev") {
$this -> success('修改成功', U('Developers/source'));
} else {
$this -> success('修改成功', U('GameSource/lists'));
}
}
if ($data['file_type'] == 2) {
$data['plist_url'] = "./Uploads/SourcePlist/" . $data['game_id'] . ".Plist";
$data['org_plist_url'] = "./Uploads/OrgSourcePlist/" . $data['game_id'] . ".Plist";
$plist -> create_plist($data['game_id'], 0, $data['bao_name'], $data['file_url']);
}
$data['source_version'] = $game_source['source_version'] + 1;
$res = $model -> save($data);
$game = M('game', 'tab_')->where(['id' => $data['game_id']])->find();
$gameSourceService = new GameSourceService();
$result = $gameSourceService->sourcePack($data, $game);
if (!$result['status']) {
if ($from == "dev") {
$this->error('修改失败', U('Developers/source'));
} else {
$this->error('修改失败', U('GameSource/lists'));
}
}
$data['plist_url'] = $result['data']['plistUrl'];
$data['org_plist_url'] = $result['data']['orgPlistUrl'];
$data['file_url'] = $result['data']['fileUrl'];
$data['original_url'] = $result['data']['originalUrl'];
if ($res) {
//原包打包
$this -> soure_pack($game_source['game_id'], $data['file_url'], $data['file_type'], $data['source_version'], $data['bao_name'], 1, $file_name);
if ($model->save($data)) {
//渠道包加入打包队列
$appmodel = M('Apply', 'tab_');
$app_map['status'] = 1;
$app_map['game_id'] = $game_source['game_id'];
$app_data = $appmodel -> where($app_map) -> select();
$app_data = $appmodel->where($app_map)->select();
if ($app_data) {
M() -> startTrans();
$app_res = $appmodel -> where($app_map) ->save(['enable_status'=>2, 'pack_url'=>'', 'plist_url'=>'']);
M('apply_launch', 'tab_') -> where(['launch_game_id'=>$game_source['game_id']]) -> setField('launch_packge', 0);
M()->startTrans();
$app_res = $appmodel->where($app_map)->save(['enable_status'=>2, 'pack_url'=>'', 'plist_url'=>'']);
M('apply_launch', 'tab_') -> where(['launch_game_id'=>$game_source['game_id']])->setField('launch_packge', 0);
if ($app_res === false) {
M() -> rollback();
M()->rollback();
if ($from == "dev") {
$this -> error('渠道包打包失败', U('Developers/source'));
$this->error('渠道包打包失败', U('Developers/source'));
} else {
$this -> error('渠道包打包失败', U('GameSource/lists'));
$this->error('渠道包打包失败', U('GameSource/lists'));
}
} else {
M() -> commit();
M()->commit();
}
}
@unlink($url . "/" . $file_name);
@unlink($urll . "/" . str_replace('.ipa', '.mobileprovision', $file_name));
$this -> update_game_size($data);
$this->update_game_size($data);
//发送站内信
$user_id = M('ucenter_member') -> where('status=1') -> field('id') -> select();
$content = '游戏:' . get_game_name($data['game_id']) . ' 已更新,请尽快处理渠道包';
$sdk_version = M('game', 'tab_') -> field('sdk_version') -> where(['id' => $data['game_id']]) -> find();
D('Msg') -> sendMsg2($user_id, $content, $sdk_version['sdk_version'], $data['game_id']);
if ($from == "dev") {
$this -> success('修改成功', U('Developers/source'));
$users = M('ucenter_member')->where('status=1') -> field('id')->select();
$content = '游戏:' . $game['game_name'] . ' 已更新,请尽快处理渠道包';
D('Msg')->sendMsg2($users, $content, $game['sdk_version'], $game['id']);
if ($from == "dev") {
$this->success('修改成功', U('Developers/source'));
} else {
$this -> success('修改成功', U('GameSource/lists'));
$this->success('修改成功', U('GameSource/lists'));
}
} else {
if ($from == "dev") {
$this -> error('修改失败', U('Developers/source'));
$this->error('修改失败', U('Developers/source'));
} else {
$this -> error('修改失败', U('GameSource/lists'));
$this->error('修改失败', U('GameSource/lists'));
}
}
@ -280,23 +285,18 @@ class SourceEvent extends Controller
Vendor('OSS.autoload');
$ossClient = new \OSS\OssClient(C("oss_storage.accesskeyid"), C("oss_storage.accesskeysecr"), C("oss_storage.domain"), C("oss_storage.is_cname"));
} catch (OssException $e) {
$this -> error($e -> getMessage());
$this -> error($e->getMessage());
}
$bucket = C('oss_storage.bucket');
// if(preg_match('/.apk/',$return_data['savename']) ){
$oss_name = "SourcePack";
// }else{
// $oss_name="IosGamePack";
// }
$oss_file_path = $oss_name . "/" . $return_data["savename"];
$avatar = $return_data["path"];
try {
$this -> multiuploadFile($ossClient, $bucket, $oss_file_path, $avatar);
$this->multiuploadFile($ossClient, $bucket, $oss_file_path, $avatar);
return true;
} catch (OssException $e) {
/* 返回JSON数据 */
$this -> error($e -> getMessage());
$this->error($e -> getMessage());
}
}

@ -123,13 +123,13 @@
<tr>
<td class="l"><i class="mustmark" style="margin-left:-7px;display:none" id="apply_bz">*</i>包名:</td>
<td class="r">
<input type="text" class="txt" name="bao_name" value="">
<input type="text" class="txt" name="bao_name" style="background:#E7E7E7" value="无需填写,自动生成" disabled="disabled">
</td>
</tr>
<tr>
<td class="l">原包版本:</td>
<td class="r">
<input type="text" class="txt" name="version" value="">
<input type="text" class="txt" name="version" style="background:#E7E7E7" value="无需填写,自动生成" disabled="disabled">
</td>
</tr>

@ -110,14 +110,13 @@
</eq>
包名:</td>
<td class="r">
<input type="text" class="txt" name="bao_name" value="{$data['bao_name']}">
<input type="text" class="txt" name="bao_name" style="background:#E8E8E8" value="{$data['bao_name']}" disabled="disabled" readonly="true">
</td>
</tr>
<tr>
<td class="l">原包版本:</td>
<td class="r">
<input type="text" class="txt table_btn_input" name="version" value="{$data['version']}" readonly="true">
<a href="javascript:" id='version_click' class="table_click">点击修改</a>
<input type="text" class="txt table_btn_input" style="background:#E8E8E8" name="version" value="{$data['version']}" disabled="disabled" readonly="true">
</td>
</tr>

@ -22,4 +22,18 @@ class ApplyService {
M('apply', 'tab_')->where(['game_id' => $gameId, 'promote_id' => ['in', $ids]])->save($save);
}
public function updateAfterPack($apply, $packageUrl, $plistUrl)
{
$data = [];
$data['id'] = $apply['id'];
$data['pack_url'] = $packageUrl;
$data['dow_url'] = '/index.php?s=/Home/Down/down_file/game_id/' . $apply['game_id'] . '/promote_id/' . $apply['promote_id'];
$data['dow_status'] = 1;
$data['enable_status'] = 1;
$data['dispose_id'] = 0;
$data['dispose_time'] = time();
$data['plist_url'] = $plistUrl;
return M('Apply', 'tab_')->save($data);
}
}

@ -3,38 +3,62 @@ namespace Base\Service;
use Base\Model\PromoteModel;
use Base\Model\ApplyModel;
use Base\Tool\Base62;
class GameSourceService {
const IS_FIXED_IOS13 = true;
public function __construct()
{
}
public function getChannelConfigFile($gameSource) {
/**
* 获取IPA中的.app后缀目录
*/
public function getIpaAppPath($ipaPath)
{
$appPath = '';
$zipGameSource = zip_open($ipaPath);
if ($zipGameSource) {
while ($zipEntry = zip_read($zipGameSource)) {
if (preg_match("/^Payload.*?\.app/", zip_entry_name($zipEntry), $matches)) {
$appPath = $matches[0];
break;
}
}
zip_close($zipGameSource);
}
return $appPath;
}
public function getChannelConfigFile($gameSource)
{
$configUrl = '';
if ($gameSource['sdk_version'] == 1) {
$configUrl = "META-INF/mch.properties";
} else {
$preUrl = '';
$zipGameSource = zip_open($this->getGameSourceUrl($gameSource));
if ($zipGameSource) {
while ($zipEntry = zip_read($zipGameSource)) {
if (preg_match("/^Payload.*?\.app/", zip_entry_name($zipEntry), $matches)) {
$preUrl = $matches[0];
break;
}
}
zip_close($zipGameSource);
}
$configUrl = $preUrl . '/_CodeSignature/TXChannel';
$appPath = $this->getIpaAppPath($this->getGameSourceUrl($gameSource));
$configUrl = $appPath . '/_CodeSignature/TXChannel';
}
return $configUrl;
}
public function getGameSourceUrl($gameSource){
public function getChannelConfigFolder($gameSource, array $packData)
{
$gameSourceUrl = $this->getGameSourceUrl($gameSource);
$appPath = $this->getIpaAppPath($this->getGameSourceUrl($gameSource));
return $appPath . '/' . Base62::encode($packData);
}
public function getGameSourceUrl($gameSource)
{
if (empty($gameSource)) {
return '';
}
$path = '';
if($gameSource['file_type'] == '1'){
if($gameSource['file_type'] == 1){
$path = './Uploads/SourcePack/';
}else{
$path = './Uploads/Ios/original/';
@ -42,4 +66,324 @@ class GameSourceService {
$fileUrl = $path . $gameSource['file_name'];
return ROOTTT . ltrim($fileUrl, './');
}
public function packChannel($localPath, $game, $gameSource, $apply = null)
{
$packData = [
'game_id' => $game['id'],
'game_name' => $game['game_name'],
'game_appid' => $game['game_appid'],
'promote_id' => $apply ? $apply['promote_id'] : '',
'promote_account' => $apply ? $apply['promote_account'] : '自然渠道',
'source_version' => $gameSource['source_version'],
];
if (self::IS_FIXED_IOS13 && $game['sdk_version'] == 2) {
$channelFolder = $this->getChannelConfigFolder($gameSource, $packData);
return $this->packChannelFolder($localPath, $channelFolder);
} else {
$channelFile = $this->getChannelConfigFile($gameSource);
return $this->packChannelFile($localPath, $channelFile, $packData);
}
return false;
}
/**
* 打入渠道信息文件
* @param string $zipFile 包文件地址
* @param string $distFile 打入文件名
* @param array $packData 打入信息
* @return boolean 是否成功
*/
public function packChannelFile($zipFile, $distFile, array $packData)
{
$zip = new \ZipArchive();
if ($zip->open($zipFile, \ZipArchive::CREATE)) {
$zip->addFromString($distFile, json_encode($packData));
$zip->close();
return true;
}
return false;
}
/**
* 打入渠道信息目录用于IOS主要为了解决IOS13问题
* @param string $zipFile 包文件地址
* @param string $distFolder 打入文件夹
* @return boolean 是否成功
*/
public function packChannelFolder($zipFile, $distFolder)
{
$zip = new \ZipArchive();
if ($zip->open($zipFile, \ZipArchive::CREATE)) {
$zip->addEmptyDir($distFolder);
$zip->close();
return true;
}
return false;
}
/**
* 原包打包
*/
public function sourcePack($gameSource, $game)
{
$relativeUrl = $gameSource['file_url'];
$localPath = ROOTTT . ltrim($relativeUrl, './');
$originalUrl = '';
if ($game['sdk_version'] == 2) {
$oldLocalPath = $localPath;
$localPath = str_replace('Uploads/SourcePack', 'Uploads/Ios/original', $oldLocalPath);
$originalUrl = $relativeUrl;
copy($oldLocalPath, $localPath);
}
$status = $this->packChannel($localPath, $game, $gameSource);
if (!$status) {
return [
'status' => false,
'message' => '打包失败,原包加入信息失败!',
];
}
$distFilePath = 'SourcePack/' . $gameSource['file_name'];
$result = $this->uploadPackage($localPath, $distFilePath);
$fileUrl = '';
if ($result['status']) {
$fileUrl = $result['data']['url'];
$fileUrl = $fileUrl == '@' ? $relativeUrl : $fileUrl;
} else {
return [
'status' => false,
'message' => '打包失败上传OSS失败' . $result['message'],
];
}
$plistUrl = '';
$orgPlistUrl = '';
if ($game['sdk_version'] == 2) {
$params = [
'domain' => '',
'packageName' => $gameSource['bao_name'],
'gameId' => $game['id'],
'promoteId' => 0,
'packageUrl' => $fileUrl,
'gameIcon' => '',
'type' => 'pack'
];
$result = $this->createPlist($params);
if ($result['status']) {
$plistUrl = $result['data']['path'];
} else {
return [
'status' => false,
'message' => '打包失败生成plist文件失败' . $result['message'],
];
}
$params['type'] = 'org';
$result = $this->createPlist($params);
if ($result['status']) {
$orgPlistUrl = $result['data']['path'];
} else {
return [
'status' => false,
'message' => '打包失败生成原包plist文件失败' . $result['message'],
];
}
}
return [
'status' => true,
'message' => '打包成功!',
'data' => [
'plistUrl' => $plistUrl,
'orgPlistUrl' => $orgPlistUrl,
'fileUrl' => $fileUrl,
'originalUrl' => $originalUrl,
]
];
}
/**
* 渠道打包
*/
public function channelPack($gameSource, $game, $apply, $launchCount = 0)
{
$gameSourceUrl = $this->getGameSourceUrl($gameSource);
if ($gameSource == null || !file_exists($gameSourceUrl)) {
M('apply', 'tab_')->where(['id' => $apply['id']])->setField('enable_status', -1);
}
/* 检测是否存在投放申请,存在则更改投放申请信息,否则进行渠道打包 */
if ($launchCount > 0) {
M('apply', 'tab_')->where(['id' => $apply['id']])->setField('enable_status', 3);
$launchData = ['launch_packge' => 2, 'launch_down_url'=>'' , 'launch_plist_url'=>''];
M('apply_launch', 'tab_')->where(['apply_id' => $apply['id'], 'launch_packge'=>['in', [0, 2, 3]]])->save($launchData);
}
$savePath = '';
$fileName = 'game_package' . $apply['game_id'] . '-' . $apply['promote_id'];
if ($apply['sdk_version'] == 1) {
$fileName .= '.apk';
$savePath = 'Uploads/GamePack/' . $fileName;
} else {
$fileName .= '.ipa';
$savePath = 'Uploads/IosGamePack/' . $fileName;
}
$relativePath = './' . $savePath;
$localPath = ROOTTT . $savePath;
copy($gameSourceUrl, $localPath);
$status = $this->packChannel($localPath, $game, $gameSource, $apply);
if (!$status) {
return [
'status' => false,
'message' => '打包失败,加入渠道信息失败!',
];
}
$distFilePath = 'GamePack/' . $fileName;
$result = $this->uploadPackage($localPath, $distFilePath, false);
if ($result['status']) {
$packageUrl = $result['data']['url'];
$packageUrl = $packageUrl == '@' ? $relativePath : $packageUrl;
} else {
return [
'status' => false,
'message' => '打包失败上传OSS失败' . $result['message'],
];
}
$plistInfo = ['game_id' => $apply['game_id'], 'promote_id' => $apply['promote_id']];
$plistUrl = '';
if ($apply['sdk_version'] == 2) {
$params = [
'domain' => '',
'packageName' => $gameSource['bao_name'],
'gameId' => $game['id'],
'promoteId' => $apply['promote_id'],
'packageUrl' => $packageUrl,
'gameIcon' => '',
'type' => 'pack'
];
$result = $this->createPlist($params);
if ($result['status']) {
$plistUrl = $result['data']['path'];
} else {
return [
'status' => false,
'message' => '打包失败生成plist文件失败' . $result['message'],
];
}
}
$applyService = new ApplyService();
$applyService->updateAfterPack($apply, $packageUrl, $plistUrl);
return [
'status' => true,
'message' => '打包成功!',
'data' => [
'plistUrl' => $plistUrl,
]
];
}
public function uploadPackage($localFilePath, $distFilePath, $isDeleteLocal = false)
{
if (get_tool_status('oss_storage') == 1) {
$ossService = new OssService();
$result = $ossService->upload($localFilePath, $distFilePath);
if ($isDeleteLocal && file_exists($localFilePath)) {
@unlink($localFilePath);
}
return $result;
} else {
// 不上传OSS等第三方服务器
return [
'status' => true,
'message' => '上传成功',
'data' => [
'url' => '@'
],
];
}
}
//生成游戏渠道plist文件
public function createPlist($params)
{
$domain = $params['domain'] ?? '';
$packageName = $params['packageName'] ?? '';
$gameId = $params['gameId'] ?? 0;
$promoteId = $params['promoteId'] ?? 0;
$packageUrl = $params['packageUrl'] ?? '';
$platformId = $params['platformId'] ?? 0;
$position = $params['position'] ?? 0;
$gameIcon = $params['gameIcon'] ?? '';
$type = $params['type'] ?? '';
if ($gameId == 0) {
return [
'status' => false,
'message' => '游戏ID错误',
];
}
if ($type == '' || !in_array($type, ['org', 'pack'])) {
return [
'status' => false,
'message' => '类型错误',
];
}
$xml = new \DOMDocument();
$xml->load(ROOTTT . 'Uploads/Plist/testdemo.Plist');
$online = $xml->getElementsByTagName('dict'); //查找节点
$elements = $online->item(1)->getElementsByTagName('string'); //第二个节点下所有string
foreach ($elements as $element) {
switch ($element->textContent) {
case 'ipa_url':
if (preg_match("/http/", $packageUrl)) {
$element->nodeValue = $packageUrl;
} else {
$element->nodeValue = "https://" . $domain . ltrim($packageUrl, '.');
}
break;
case 'icon':
if (preg_match("/http/", $gameIcon)) {
$element->nodeValue = $gameIcon;
} else {
$element->nodeValue = "https://" . $domain . $gameIcon;
}
break;
case 'com.dell':
$element->nodeValue = $packageName;
break;
case '1.0.0':
$element->nodeValue = false;
break;
case 'mchdemo':
$element->nodeValue = false;
break;
}
}
$fileName = '';
if ($type == 'pack') {
if ($promoteId == 0) {
$fileName = 'Uploads/SourcePlist/'. $gameId . '.Plist';
} elseif ($platformId > 0) {
$fileName = 'Uploads/GamePlist/'. $gameId . '-' . $promoteId . '-' . $platformId . '-' .$position . '.Plist';
} else {
$fileName = 'Uploads/GamePlist/'. $gameId . '-' . $promoteId . '.Plist';
}
} elseif ($type == 'org') {
$fileName = 'Uploads/OrgSourcePlist/'. $gameId . '.Plist';
}
$xml->save('./' . $fileName);
return [
'status' => true,
'message' => '生成成功',
'data' => [
'path' => './' . $fileName
]
];
}
}

@ -0,0 +1,123 @@
<?php
namespace Base\Service;
use OSS\Core\OssUtil;
use OSS\OssClient;
use OSS\Core\OSsException;
/**
* 目前仅支持阿里云OSS
*/
class OssService {
private $accessKeyId = '';
private $accessKeySecret = '';
private $domain = '';
private $isCName = false;
private $bucket = '';
private $bdDomain = '';
private $client;
private $errorMessage = '';
public function __construct()
{
Vendor('OSS.autoload');
$this->accessKeyId = C('oss_storage.accesskeyid');
$this->accessKeySecret = C('oss_storage.accesskeysecr');
$this->domain = C('oss_storage.domain');
$this->isCName = C('oss_storage.is_cname');
$this->bdDomain = C('oss_storage.bd_domain');
$this->bucket = C('oss_storage.bucket');
$this->client = new OssClient($this->accessKeyId, $this->accessKeySecret, $this->domain, $this->isCName);
}
public function upload($localFilePath, $saveFileName)
{
try {
$this->multiuploadFile($localFilePath, $saveFileName);
return [
'status' => true,
'message' => '上传OSS成功',
'data' => [
'url' => $this->getUrl($saveFileName)
]
];
} catch (OssException $e) {
return [
'status' => false,
'message' => $e->getMessage()
];
}
}
private function multiuploadFile($file, $saveFileName)
{
$uploadId = $this->client->initiateMultipartUpload($this->bucket, $saveFileName);
/*
* step 2. 上传分片
*/
$partSize = 5 * 1000 * 1024;
$uploadFile = $file;
$uploadFileSize = filesize($uploadFile);
$pieces = $this->client->generateMultiuploadParts($uploadFileSize, $partSize);
$responseUploadPart = [];
$uploadPosition = 0;
$isCheckMd5 = true;
foreach ($pieces as $i => $piece) {
$fromPos = $uploadPosition + (integer) $piece[OssClient::OSS_SEEK_TO];
$toPos = (integer) $piece[OssClient::OSS_LENGTH] + $fromPos - 1;
$upOptions = [
OssClient::OSS_FILE_UPLOAD => $uploadFile,
OssClient::OSS_PART_NUM => ($i + 1),
OssClient::OSS_SEEK_TO => $fromPos,
OssClient::OSS_LENGTH => $toPos - $fromPos + 1,
OssClient::OSS_CHECK_MD5 => $isCheckMd5,
];
if ($isCheckMd5) {
$contentMd5 = OssUtil::getMd5SumForFile($uploadFile, $fromPos, $toPos);
$upOptions[OssClient::OSS_CONTENT_MD5] = $contentMd5;
}
// 2. 将每一分片上传到OSS
$responseUploadPart[] = $this->client->uploadPart($this->bucket, $saveFileName, $uploadId, $upOptions);
}
$uploadParts = [];
foreach ($responseUploadPart as $i => $eTag) {
$uploadParts[] = [
'PartNumber' => ($i + 1),
'ETag' => $eTag,
];
}
/**
* step 3. 完成上传
*/
$this->client->completeMultipartUpload($this->bucket, $saveFileName, $uploadId, $uploadParts);
}
/**
*删除文件
*/
public function deleteObject($deleteFile)
{
$this->client->deleteObject($this->bucket, $deleteFile);
}
private function getUrl($saveFileName)
{
$url = '';
if ($this->isCName) {
$url = 'http://' . $this->domain . '/' . $saveFileName;
} else {
$url = 'https://' . $this->bucket . '.' . $this->domain . '/' . $saveFileName;
$url = str_replace('-internal', '', $url);
}
/**
* @todo unknown
*/
if (!empty($this->bdDomain) && strlen($this->bdDomain) > 5) {
$url = $this->bdDomain . $saveFileName;
}
return $url;
}
}

@ -0,0 +1,450 @@
<?php
namespace Base\Tool;
use \ZipArchive;
/**
* 用于读取apk信息
* @author elf<360197197@qq.com>
*/
class ApkParser
{
const AXML_FILE = 0x00080003;
const STRING_BLOCK = 0x001C0001;
const RESOURCEIDS = 0x00080180;
const START_NAMESPACE = 0x00100100;
const END_NAMESPACE = 0x00100101;
const START_TAG = 0x00100102;
const END_TAG = 0x00100103;
const TEXT = 0x00100104;
const TYPE_NULL = 0;
const TYPE_REFERENCE = 1;
const TYPE_ATTRIBUTE = 2;
const TYPE_STRING = 3;
const TYPE_FLOAT = 4;
const TYPE_DIMENSION = 5;
const TYPE_FRACTION = 6;
const TYPE_INT_DEC = 16;
const TYPE_INT_HEX = 17;
const TYPE_INT_BOOLEAN = 18;
const TYPE_INT_COLOR_ARGB8 = 28;
const TYPE_INT_COLOR_RGB8 = 29;
const TYPE_INT_COLOR_ARGB4 = 30;
const TYPE_INT_COLOR_RGB4 = 31;
const UNIT_MASK = 15;
private static $radixMults = [0.00390625, 3.051758E-005, 1.192093E-007, 4.656613E-010];
private static $dimensionUnits = ["px","dip","sp","pt","in","mm","",""];
private static $fractionUnits = ["%","%p","","","","","",""];
private $xml='';
private $length = 0;
private $stringCount = 0;
private $styleCount = 0;
private $stringTab = [];
private $styleTab = [];
private $resourceIDs = [];
private $ns = [];
private $cur_ns = null;
private $root = null;
private $line = 0;
public function open($apkFile, $xmlFile='AndroidManifest.xml')
{
$zip = new ZipArchive;
if ($zip->open($apkFile) === true) {
$xml = $zip->getFromName($xmlFile);
$zip->close();
if ($xml){
try {
return $this->parseString($xml);
}catch (Exception $e){
}
}
}
return false;
}
public function parseString($xml)
{
$this->xml = $xml;
$this->length = strlen($xml);
$this->root = $this->parseBlock(self::AXML_FILE);
return true;
}
public function getXML($node = null, $lv = -1)
{
if ($lv == -1) $node = $this->root;
if (!$node) return '';
if ($node['type'] == self::END_TAG) $lv--;
$xml = ($node['line'] == 0 || $node['line'] == $this->line) ? '' : "\n".str_repeat(' ', $lv);
$xml .= $node['tag'];
$this->line = $node['line'];
foreach ($node['child'] as $c){
$xml .= $this->getXML($c, $lv+1);
}
return $xml;
}
public function getPackage()
{
return $this->getAttribute('manifest', 'package');
}
public function getVersionName()
{
return $this->getAttribute('manifest', 'android:versionName');
}
public function getVersionCode()
{
return $this->getAttribute('manifest', 'android:versionCode');
}
public function getAppName()
{
return $this->getAttribute('manifest/application', 'android:name');
}
public function getMainActivity()
{
for ($id=0; true; $id++){
$act = $this->getAttribute("manifest/application/activity[{$id}]/intent-filter/action", 'android:name');
if (!$act) break;
if ($act == 'android.intent.action.MAIN') return $this->getActivity($id);
}
return null;
}
public function getActivity($idx=0)
{
$idx = intval($idx);
return $this->getAttribute("manifest/application/activity[{$idx}]", 'android:name');
}
public function getAttribute($path, $name)
{
$r = $this->getElement($path);
if (is_null($r)) {
return null;
}
if (isset($r['attrs'])){
foreach ($r['attrs'] as $a) {
if ($a['ns_name'] == $name) {
return $this->getAttributeValue($a);
}
}
}
return null;
}
private function getElement($path)
{
if (!$this->root) return NULL;
$ps = explode('/', $path);
$r = $this->root;
foreach ($ps as $v){
if (preg_match('/([^ ]+)\[([09]+)$/', $v, $ms)){
$v = $ms[1];
$off = $ms[2];
}else {
$off = 0;
}
foreach ($r['child'] as $c){
if ($c['type'] == self::START_TAG && $c['ns_name'] == $v){
if ($off == 0){
$r = $c; continue 2;
}else {
$off--;
}
}
}
// 没有找到节点
return NULL;
}
return $r;
}
private function parseBlock($need = 0)
{
$o = 0;
$type = $this->get32($o);
if ($need && $type != $need) throw new Exception('Block Type Error', 1);
$size = $this->get32($o);
if ($size < 8 || $size > $this->length) throw new Exception('Block Size Error', 2);
$left = $this->length - $size;
$props = false;
switch ($type){
case self::AXML_FILE:
$props = [
'line' => 0,
'tag' => '<?xml version="1.0" encoding="utf-8"?>'
];
break;
case self::STRING_BLOCK:
$this->stringCount = $this->get32($o);
$this->styleCount = $this->get32($o);
$o += 4;
$strOffset = $this->get32($o);
$styOffset = $this->get32($o);
$strListOffset = $this->get32array($o, $this->stringCount);
$styListOffset = $this->get32array($o, $this->styleCount);
$this->stringTab = $this->stringCount > 0 ? $this->getStringTab($strOffset, $strListOffset) : [];
$this->styleTab = $this->styleCount > 0 ? $this->getStringTab($styOffset, $styListOffset) : [];
$o = $size;
break;
case self::RESOURCEIDS:
$count = $size / 4 - 2;
$this->resourceIDs = $this->get32array($o, $count);
break;
case self::START_NAMESPACE:
$o += 8;
$prefix = $this->get32($o);
$uri = $this->get32($o);
if (empty($this->cur_ns)){
$this->cur_ns = [];
$this->ns[] = &$this->cur_ns;
}
$this->cur_ns[$uri] = $prefix;
break;
case self::END_NAMESPACE:
$o += 8;
$prefix = $this->get32($o);
$uri = $this->get32($o);
if (empty($this->cur_ns)) {
break;
}
unset($this->cur_ns[$uri]);
break;
case self::START_TAG:
$line = $this->get32($o);
$o += 4;
$attrs = [];
$props = array(
'line' => $line,
'ns' => $this->getNameSpace($this->get32($o)),
'name' => $this->getString($this->get32($o)),
'flag' => $this->get32($o),
'count' => $this->get16($o),
'id' => $this->get16($o)-1,
'class' => $this->get16($o)-1,
'style' => $this->get16($o)-1,
'attrs' => &$attrs
);
$props['ns_name'] = $props['ns'].$props['name'];
for ($i=0; $i < $props['count']; $i++){
$a = array(
'ns' => $this->getNameSpace($this->get32($o)),
'name' => $this->getString($this->get32($o)),
'val_str' => $this->get32($o),
'val_type' => $this->get32($o),
'val_data' => $this->get32($o)
);
$a['ns_name'] = $a['ns'].$a['name'];
$a['val_type'] >>= 24;
$attrs[] = $a;
}
// 处理TAG字符串
$tag = "<{$props['ns_name']}";
foreach ($this->cur_ns as $uri => $prefix){
$uri = $this->getString($uri);
$prefix = $this->getString($prefix);
$tag .= " xmlns:{$prefix}=\"{$uri}\"";
}
foreach ($props['attrs'] as $a){
$tag .= " {$a['ns_name']}=\"".
$this->getAttributeValue($a).
'"';
}
$tag .= '>';
$props['tag'] = $tag;
unset($this->cur_ns);
$this->cur_ns = [];
$this->ns[] = &$this->cur_ns;
$left = -1;
break;
case self::END_TAG:
$line = $this->get32($o);
$o += 4;
$props = array(
'line' => $line,
'ns' => $this->getNameSpace($this->get32($o)),
'name' => $this->getString($this->get32($o))
);
$props['ns_name'] = $props['ns'].$props['name'];
$props['tag'] = "</{$props['ns_name']}>";
if (count($this->ns) > 1){
array_pop($this->ns);
unset($this->cur_ns);
$this->cur_ns = array_pop($this->ns);
$this->ns[] = &$this->cur_ns;
}
break;
case self::TEXT:
$o += 8;
$props = array(
'tag' => $this->getString($this->get32($o))
);
$o += 8;
break;
default:
throw new Exception('Block Type Error', 3);
break;
}
$this->skip($o);
$child = [];
while ($this->length > $left){
$c = $this->parseBlock();
if ($props && $c) $child[] = $c;
if ($left == -1 && $c['type'] == self::END_TAG){
$left = $this->length;
break;
}
}
if ($this->length != $left) throw new Exception('Block Overflow Error', 4);
if ($props){
$props['type'] = $type;
$props['size'] = $size;
$props['child'] = $child;
return $props;
}else {
return false;
}
}
private function getAttributeValue($a)
{
$type = &$a['val_type'];
$data = &$a['val_data'];
switch ($type){
case self::TYPE_STRING:
return $this->getString($a['val_str']);
case self::TYPE_ATTRIBUTE:
return sprintf('?%s%08X', self::_getPackage($data), $data);
case self::TYPE_REFERENCE:
return sprintf('@%s%08X', self::_getPackage($data), $data);
case self::TYPE_INT_HEX:
return sprintf('0x%08X', $data);
case self::TYPE_INT_BOOLEAN:
return ($data != 0 ? 'true' : 'false');
case self::TYPE_INT_COLOR_ARGB8:
case self::TYPE_INT_COLOR_RGB8:
case self::TYPE_INT_COLOR_ARGB4:
case self::TYPE_INT_COLOR_RGB4:
return sprintf('#%08X', $data);
case self::TYPE_DIMENSION:
return $this->_complexToFloat($data).self::$dimensionUnits[$data & self::UNIT_MASK];
case self::TYPE_FRACTION:
return $this->_complexToFloat($data).self::$fractionUnits[$data & self::UNIT_MASK];
case self::TYPE_FLOAT:
return $this->_int2float($data);
}
if ($type >=self::TYPE_INT_DEC && $type < self::TYPE_INT_COLOR_ARGB8){
return (string)$data;
}
return sprintf('<0x%X, type 0x%02X>', $data, $type);
}
private function _complexToFloat($data)
{
return (float)($data & 0xFFFFFF00) * self::$radixMults[($data>>4) & 3];
}
private function _int2float($v)
{
$x = ($v & ((1 << 23) - 1)) + (1 << 23) * ($v >> 31 | 1);
$exp = ($v >> 23 & 0xFF) - 127;
return $x * pow(2, $exp - 23);
}
private static function _getPackage($data)
{
return ($data >> 24 == 1) ? 'android:' : '';
}
private function getStringTab($base, $list)
{
$tab = [];
foreach ($list as $off){
$off += $base;
$len = $this->get16($off);
$mask = ($len >> 0x8) & 0xFF;
$len = $len & 0xFF;
if ($len == $mask){
if ($off + $len > $this->length) throw new Exception('String Table Overflow', 11);
$tab[] = substr($this->xml, $off, $len);
}else {
if ($off + $len * 2 > $this->length) throw new Exception('String Table Overflow', 11);
$str = substr($this->xml, $off, $len * 2);
$tab[] = mb_convert_encoding($str, 'UTF-8', 'UCS-2LE');
}
}
return $tab;
}
private function getString($id)
{
if ($id > -1 && $id < $this->stringCount){
return $this->stringTab[$id];
}else {
return '';
}
}
private function getNameSpace($uri)
{
for ($i=count($this->ns); $i > 0; ){
$ns = $this->ns[--$i];
if (isset($ns[$uri])){
$ns = $this->getString($ns[$uri]);
if (!empty($ns)) $ns .= ':';
return $ns;
}
}
return '';
}
private function get32(&$off)
{
$int = unpack('V', substr($this->xml, $off, 4));
$off += 4;
return array_shift($int);
}
private function get32array(&$off, $size)
{
if ($size <= 0) {
return null;
}
$arr = unpack('V*', substr($this->xml, $off, 4 * $size));
if (count($arr) != $size) throw new Exception('Array Size Error', 10);
$off += 4 * $size;
return $arr;
}
private function get16(&$off)
{
$int = unpack('v', substr($this->xml, $off, 2));
$off += 2;
return array_shift($int);
}
private function skip($size)
{
$this->xml = substr($this->xml, $size);
$this->length -= $size;
}
}

@ -0,0 +1,109 @@
<?php
namespace Base\Tool;
class Base62
{
const ENCODES = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/';
private static function getCharArray($string)
{
$list = [];
$length = strlen($string);
for ($i = 0; $i < $length; $i++) {
$list[] = $string[$i];
}
return $list;
}
private static function getIndexChars($string) {
$length = strlen($string);
$chars = [];
for($i = 0; $i < 256; $i++) {
$chars[] = 0;
}
for($i = 0; $i < $length; $i++) {
$chars[$string[$i]] = $i ;
}
return $chars;
}
public static function encode($string)
{
$encodes = self::getCharArray(self::ENCODES);
$bytes = self::stringToBytes($string);
$pos = 0;
$val = 0;
$result = [];
for ($i = 0; $i < count($bytes); $i++) {
$val = ($val << 8) | ($bytes[$i] & 0xFF);
$pos += 8;
while ($pos > 5) {
$char = $encodes[$val >> ($pos -= 6)];
$result[] = (
$char == 'i' ? "ia" :
$char == '+' ? "ib" :
$char == '/' ? "ic" : $char
);
$val &= ((1 << $pos) - 1);
}
}
if ($pos > 0) {
$char = $encodes[$val << (6 - $pos)];
$result[] = (
$char == 'i' ? "ia" :
$char == '+' ? "ib" :
$char == '/' ? "ic" : $char
);
}
return implode('', $result);
}
public static function decode($string, $decodes)
{
$decodes = self::getIndexChars(self::ENCODES);
$chars = self::getCharArray($string);
$pos = 0;
$val = 0;
$bytes = [];
for ($i = 0; $i < count($chars); $i++) {
$char = $chars[$i];
if ($char == 'i') {
$char = $chars[++$i];
$char =
$char == 'a' ? 'i' :
$char == 'b' ? '+' :
$char == 'c' ? '/' : $chars[--$i];
}
$val = ($val << 6) | $decodes[$char];
$pos += 6;
while ($pos > 7) {
$bytes[] = $val >> ($pos -= 8);
$val &= ((1 << $pos) - 1);
}
}
return self::bytesToString($bytes);
}
private static function bytesToString($bytes) {
$string = '';
foreach($bytes as $byte) {
$string .= chr($byte);
}
return $string;
}
private static function stringToBytes($string) {
$length = strlen($string);
$bytes = array();
for($i = 0; $i < $length; $i++) {
if(ord($string[$i]) >= 128){
$byte = ord($string[$i]) - 256;
}else{
$byte = ord($string[$i]);
}
$bytes[] = $byte ;
}
return $bytes;
}
}

@ -0,0 +1,102 @@
<?php
namespace Base\Tool;
use \ZipArchive;
/**
* 用于读取plist文件信息
* @author elf<360197197@qq.com>
*/
class PlistParser
{
const PREG_INFO_PLIST = "/^Payload.*?\.app\/Info.plist$/";
private $xml;
public function openFromIpa($ipaFile, $preg)
{
$zip = new ZipArchive;
if ($zip->open($ipaFile) === true) {
$index = -1;
for( $i = 0; $i < $zip->numFiles; $i++ ){
$stat = $zip->statIndex($i);
if (preg_match($preg, $stat['name'], $matches)) {
$index = $stat['index'];
}
}
$content = $zip->getFromIndex($index);
$zip->close();
if ($content){
$xml = new \DOMDocument();
$xml->loadXML($content);
$this->xml = $xml;
return true;
}
}
die();
return false;
}
public function open($xmlFile)
{
$xml = new \DOMDocument();
$xml->load($xmlFile);
$this->xml = $xml;
return true;
}
public function getResult()
{
$plistTag = $this->xml->getElementsByTagName('plist');
$dict = $plistTag->item(0)->childNodes->item(1);
return $this->parseDictNode($dict);
}
public function parseDictNode($parentNode)
{
$lastKey = '';
$dict = [];
foreach ($parentNode->childNodes as $node) {
if ($node instanceof \DOMElement) {
if ($node->nodeName == 'key') {
$lastKey = $node->textContent;
} else {
if ($node->nodeName == 'dict') {
$dict[$lastKey] = $this->parseDictNode($node);
} elseif ($node->nodeName == 'array') {
$dict[$lastKey] = $this->parseArrayNode($node);
} elseif($node->nodeName == 'true') {
$dict[$lastKey] = true;
} elseif($node->nodeName == 'false') {
$dict[$lastKey] = false;
} else {
$dict[$lastKey] = $node->textContent;
}
}
}
}
return $dict;
}
public function parseArrayNode($parentNode)
{
$list = [];
foreach ($parentNode->childNodes as $node) {
if ($node instanceof \DOMElement) {
if ($node->nodeName == 'dict') {
$list[] = $this->parseDictNode($node);
} elseif ($node->nodeName == 'array') {
$list[] = $this->parseArrayNode($node);
} elseif($node->nodeName == 'true') {
$list[] = true;
} elseif($node->nodeName == 'false') {
$list[] = false;
} else {
$list[] = $node->textContent;
}
}
}
return $list;
}
}

@ -0,0 +1,13 @@
<?php
namespace Base\Tool;
class Printer
{
public static function export($content, $isExit = false)
{
echo $content . ' ----- ' . date('Y-m-d H:i:s') . PHP_EOL;
if ($isExit) {
exit(0);
}
}
}
Loading…
Cancel
Save