<?php
namespace aapt;
/**
* 利用aapt解析apk文件
* 需要依赖aapt工具
* 需要依赖PHP的ZIP包函数支持。
*/
class AaptParseApk{
const DS=DIRECTORY_SEPARATOR;
private $aaptPath;//aapt 应用程序
private $apkPath;//apk文件绝对路径
private $command;
private $commandString="%AAPTPATH% d %COMMANDBODY% %APKFILEPATH% ";
private $commandType = array(
'BADGING'=>'badging', // 获取apk详细信息
'PERMISSIONS'=>'permissions', //获取权限信息
//'XMLTREE'=>'xmltree', //
);
public $app;
private $error;
public function __construct($aaptPath='',$apkPath=null){
empty($aaptPath)?
$this->aaptPath=strstr(PHP_OS, 'WIN')?dirname(__FILE__).'/aapt':C('AAPT_PATH')
:$this->aaptPath=strtr($aaptPath,'\\','/').'/aapt';
/*empty($aaptPath)?
$this->aaptPath=strstr(PHP_OS,'WIN')?'aapt':'./aapt':
$this->aaptPath=strtr($aaptPath,'\\','/').'/aapt';*/
$this->apkPath = $apkPath;
}
/**
* 根据apk路径通过aapt获取信息
* @param string $apkPath
* @param string $type
*/
public function getApkInfo($apkPath='',$type='BADGING'){
empty($apkPath) or $this->apkPath = $apkPath;
$this->apkPath = realpath($this->apkPath);
if( !is_file($this->apkPath) ){
$this->error='文件路径不合法';
}
if(empty($this->apkPath)){
$this->error='APK文件为空';
return false;
}
empty($type) or $type=strtoupper($type);
if(!in_array(strtoupper($type),array_keys($this->commandType))){
$this->error='非法的命令类型';
return false;
}
$replaceArray = array(
'%AAPTPATH%'=>$this->aaptPath,
'%COMMANDBODY%'=>$this->commandType[$type],
'%APKFILEPATH%'=>$this->apkPath,
);
$this->command = strtr($this->commandString,$replaceArray);
if($type=='XMLTREE'){
$this->command .= ' AndroidManifest.xml';
}
//APP_DEBUG && \Think\Log::write($this->command,'AAPT_COMMAND');
$result = $this->exec($this->command);
if( empty($this->error) ){
return $this->praseData($result);
}
return false;
}
/**
* 解析在apk包中获取的数据
* @param array $data
*/
public function praseData($data){
//package: name='cn.falconnect.shopping.cat' versionCode='10' versionName='1.0 (ZN)' platformBuildVersionName=' ' ;
if( is_array($data) ){
foreach($data as $key=>$line){
//取得包名,版本信息
if($key==0 && preg_match("#^package: name='([a-zA-Z0-9.]+)' versionCode='([\d]+)' versionName='([^']+)'.*?$#",$line,$package) ){
empty($package[1]) or $this->app['package_name'] = $package[1];
empty($package[2]) or $this->app['version_code'] = $package[2];
empty($package[3]) or $this->app['version_name'] = $package[3];
unset($package);
}
//取得最低系统版本要求 例:Android 2.3 以上
if(preg_match("#^minSdkVersion:'([\d]+)'#",$line,$sdkversion) || preg_match("#^sdkVersion:'([^\']+)'$#",$line,$sdkversion)){
empty($sdkversion) or $this->app['sdk_version'] = $sdkversion[1];
}
//取得权限信息
if( preg_match("#^uses-permission:'android.permission.([A-Z_]+)'$#",$line,$permission) ){
empty($permission[1]) or $this->app['permissions'][] = $permission[1];
}
//取得应用名称,icon在apk包里的路径
if( preg_match("#^application: label='([\x{4e00}-\x{9fa5}A-Za-z0-9_\s]+)' icon='([0-9A-Za-z._/\-]+)'$#u",$line,$labelicon) ){
empty($labelicon[1]) or $this->app['app_name'] = trim($labelicon[1]);
empty($labelicon[2]) or $this->app['icon'] = $labelicon[2];
}
}
$this->app['apk_path'] = $this->apkPath;
$this->app['size'] = $this->app['size_mb'] = 0;
$this->app['size'] = filesize($this->apkPath);
$this->app['size'] && $this->app['size_mb'] = round($this->app['size'] /1024/1024,2);
//$this->app['hash'] = md5_file($this->apkPath);
return $this->app;
}
$this->error='取出的数据不正确';
return false;
}
/**
* 获取apk文件中的icon
* @param string $extroTo 输出icon到哪个路径
* @param string $apkIconPath apk包中icon的路径
* @param string $apkPath apk包的路径
* @return string
*/
public function praseIcon($extroTo,$apkIconPath,$apkPath=''){
!empty($apkPath) or $apkPath = $this->apkPath;
if(empty($apkPath)){
$this->error='apk文件路径为空';
return false;
}
if( !is_file($apkPath) ){
$this->error='文件路径不正确';
return false;
}
if ( !extension_loaded('zip') ) {
throw new \Exception('zip扩展没有安装');
}
$zip = new \ZipArchive();
if($zip->open($apkPath)===TRUE){
if( TRUE===$zip->extractTo($extroTo,$apkIconPath) ){
if(rename($extroTo.$apkIconPath,$extroTo.basename($apkIconPath)) ){
$dirpath=$apkIconPath;
$level=explode('/',dirname($dirpath));
foreach($level as $dir){
rmdir($extroTo.dirname($dirpath));
$dirpath=dirname($dirpath);
}
return $extroTo.basename($apkIconPath);
}
return $extroTo.$apkIconPath;
}else{
$this->error='解压文件失败';
return false;
}
$zip->close();
}else{
$this->error='apk文件打开失败';
return false;
}
return;
}
private function deldir($dir){
}
private function exec($command='',$isReturnErrString=1){
empty($isReturnErrString) or $command=$command.'2>&1';
strstr(PHP_OS, 'WIN') or chmod($this->apkPath, 0755);
exec($command,$output,$code);
if( empty($code) ){
return $output;
}
//file_put_contents("mylog2.html",$command."|".$output."|".$code);
$this->error = $code.','.trim(implode(' ',$output) );
return false;
}
private function passthru($command,$isEcho=0){
$command .= '2>&1';
if($isEcho){
passthru($command,$return);
return;
}
ob_start();
passthru($command,$return);
$output = ob_get_contents();
ob_end_clean();
if(strpos('ERROR:',$output)===FALSE){
return array_filter(explode(PHP_EOL,$output));
}
$this->error = $output;
return false;
}
public function getCommand(){
return $this->command;
}
public function getError(){
return $this->error;
}
}
namespace aapt;
/**
* 利用aapt解析apk文件
* 需要依赖aapt工具
* 需要依赖PHP的ZIP包函数支持。
*/
class AaptParseApk{
const DS=DIRECTORY_SEPARATOR;
private $aaptPath;//aapt 应用程序
private $apkPath;//apk文件绝对路径
private $command;
private $commandString="%AAPTPATH% d %COMMANDBODY% %APKFILEPATH% ";
private $commandType = array(
'BADGING'=>'badging', // 获取apk详细信息
'PERMISSIONS'=>'permissions', //获取权限信息
//'XMLTREE'=>'xmltree', //
);
public $app;
private $error;
public function __construct($aaptPath='',$apkPath=null){
empty($aaptPath)?
$this->aaptPath=strstr(PHP_OS, 'WIN')?dirname(__FILE__).'/aapt':C('AAPT_PATH')
:$this->aaptPath=strtr($aaptPath,'\\','/').'/aapt';
/*empty($aaptPath)?
$this->aaptPath=strstr(PHP_OS,'WIN')?'aapt':'./aapt':
$this->aaptPath=strtr($aaptPath,'\\','/').'/aapt';*/
$this->apkPath = $apkPath;
}
/**
* 根据apk路径通过aapt获取信息
* @param string $apkPath
* @param string $type
*/
public function getApkInfo($apkPath='',$type='BADGING'){
empty($apkPath) or $this->apkPath = $apkPath;
$this->apkPath = realpath($this->apkPath);
if( !is_file($this->apkPath) ){
$this->error='文件路径不合法';
}
if(empty($this->apkPath)){
$this->error='APK文件为空';
return false;
}
empty($type) or $type=strtoupper($type);
if(!in_array(strtoupper($type),array_keys($this->commandType))){
$this->error='非法的命令类型';
return false;
}
$replaceArray = array(
'%AAPTPATH%'=>$this->aaptPath,
'%COMMANDBODY%'=>$this->commandType[$type],
'%APKFILEPATH%'=>$this->apkPath,
);
$this->command = strtr($this->commandString,$replaceArray);
if($type=='XMLTREE'){
$this->command .= ' AndroidManifest.xml';
}
//APP_DEBUG && \Think\Log::write($this->command,'AAPT_COMMAND');
$result = $this->exec($this->command);
if( empty($this->error) ){
return $this->praseData($result);
}
return false;
}
/**
* 解析在apk包中获取的数据
* @param array $data
*/
public function praseData($data){
//package: name='cn.falconnect.shopping.cat' versionCode='10' versionName='1.0 (ZN)' platformBuildVersionName=' ' ;
if( is_array($data) ){
foreach($data as $key=>$line){
//取得包名,版本信息
if($key==0 && preg_match("#^package: name='([a-zA-Z0-9.]+)' versionCode='([\d]+)' versionName='([^']+)'.*?$#",$line,$package) ){
empty($package[1]) or $this->app['package_name'] = $package[1];
empty($package[2]) or $this->app['version_code'] = $package[2];
empty($package[3]) or $this->app['version_name'] = $package[3];
unset($package);
}
//取得最低系统版本要求 例:Android 2.3 以上
if(preg_match("#^minSdkVersion:'([\d]+)'#",$line,$sdkversion) || preg_match("#^sdkVersion:'([^\']+)'$#",$line,$sdkversion)){
empty($sdkversion) or $this->app['sdk_version'] = $sdkversion[1];
}
//取得权限信息
if( preg_match("#^uses-permission:'android.permission.([A-Z_]+)'$#",$line,$permission) ){
empty($permission[1]) or $this->app['permissions'][] = $permission[1];
}
//取得应用名称,icon在apk包里的路径
if( preg_match("#^application: label='([\x{4e00}-\x{9fa5}A-Za-z0-9_\s]+)' icon='([0-9A-Za-z._/\-]+)'$#u",$line,$labelicon) ){
empty($labelicon[1]) or $this->app['app_name'] = trim($labelicon[1]);
empty($labelicon[2]) or $this->app['icon'] = $labelicon[2];
}
}
$this->app['apk_path'] = $this->apkPath;
$this->app['size'] = $this->app['size_mb'] = 0;
$this->app['size'] = filesize($this->apkPath);
$this->app['size'] && $this->app['size_mb'] = round($this->app['size'] /1024/1024,2);
//$this->app['hash'] = md5_file($this->apkPath);
return $this->app;
}
$this->error='取出的数据不正确';
return false;
}
/**
* 获取apk文件中的icon
* @param string $extroTo 输出icon到哪个路径
* @param string $apkIconPath apk包中icon的路径
* @param string $apkPath apk包的路径
* @return string
*/
public function praseIcon($extroTo,$apkIconPath,$apkPath=''){
!empty($apkPath) or $apkPath = $this->apkPath;
if(empty($apkPath)){
$this->error='apk文件路径为空';
return false;
}
if( !is_file($apkPath) ){
$this->error='文件路径不正确';
return false;
}
if ( !extension_loaded('zip') ) {
throw new \Exception('zip扩展没有安装');
}
$zip = new \ZipArchive();
if($zip->open($apkPath)===TRUE){
if( TRUE===$zip->extractTo($extroTo,$apkIconPath) ){
if(rename($extroTo.$apkIconPath,$extroTo.basename($apkIconPath)) ){
$dirpath=$apkIconPath;
$level=explode('/',dirname($dirpath));
foreach($level as $dir){
rmdir($extroTo.dirname($dirpath));
$dirpath=dirname($dirpath);
}
return $extroTo.basename($apkIconPath);
}
return $extroTo.$apkIconPath;
}else{
$this->error='解压文件失败';
return false;
}
$zip->close();
}else{
$this->error='apk文件打开失败';
return false;
}
return;
}
private function deldir($dir){
}
private function exec($command='',$isReturnErrString=1){
empty($isReturnErrString) or $command=$command.'2>&1';
strstr(PHP_OS, 'WIN') or chmod($this->apkPath, 0755);
exec($command,$output,$code);
if( empty($code) ){
return $output;
}
//file_put_contents("mylog2.html",$command."|".$output."|".$code);
$this->error = $code.','.trim(implode(' ',$output) );
return false;
}
private function passthru($command,$isEcho=0){
$command .= '2>&1';
if($isEcho){
passthru($command,$return);
return;
}
ob_start();
passthru($command,$return);
$output = ob_get_contents();
ob_end_clean();
if(strpos('ERROR:',$output)===FALSE){
return array_filter(explode(PHP_EOL,$output));
}
$this->error = $output;
return false;
}
public function getCommand(){
return $this->command;
}
public function getError(){
return $this->error;
}
}