php获取apk包信息的方法
有时候在使用php上传安卓apk包的时候,我们需要获取安卓apk包内的信息,本文以实例形式讲述了php获取apk包信息的方法。具体实现方法如下:
<?php
/*解析安卓apk包中的压缩XML文件,还原和读取XML内容
依赖功能:需要PHP的ZIP包函数支持。*/
include('./Apkparser.php');
$appObj=newApkparser();
$targetFile=a.apk;//apk所在的路径地址
$res=$appObj->open($targetFile);
$appObj->getAppName();//应用名称
$appObj->getPackage();//应用包名
$appObj->getVersionName();//版本名称
$appObj->getVersionCode();//版本代码
?>
以下是Apkparser类包,把以下代码复制出来保存为Apkparser.php就可以执行以上代码
<?php
//-------------------------------
//Apkparser类包开始
//-------------------------------
classApkParser{
//----------------------
//公共函数,供外部调用
//----------------------
publicfunctionopen($apk_file,$xml_file='AndroidManifest.xml'){
$zip=new\ZipArchive;
if($zip->open($apk_file)===TRUE){
$xml=$zip->getFromName($xml_file);
$zip->close();
if($xml){
try{
return$this->parseString($xml);
}catch(Exception$e){
}
}
}
returnfalse;
}
publicfunctionparseString($xml){
$this->xml=$xml;
$this->length=strlen($xml);
$this->root=$this->parseBlock(self::AXML_FILE);
returntrue;
}
publicfunctiongetXML($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;
}
publicfunctiongetPackage(){
return$this->getAttribute('manifest','package');
}
publicfunctiongetVersionName(){
return$this->getAttribute('manifest','android:versionName');
}
publicfunctiongetVersionCode(){
return$this->getAttribute('manifest','android:versionCode');
}
publicfunctiongetAppName(){
return$this->getAttribute('manifest/application','android:name');
}
publicfunctiongetMainActivity(){
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);
}
returnNULL;
}
publicfunctiongetActivity($idx=0){
$idx=intval($idx);
return$this->getAttribute("manifest/application/activity[{$idx}]",'android:name');
}
publicfunctiongetAttribute($path,$name){
$r=$this->getElement($path);
if(is_null($r))returnNULL;
if(isset($r['attrs'])){
foreach($r['attrs']as$a){
if($a['ns_name']==$name)return$this->getAttributeValue($a);
}
}
returnNULL;
}
//----------------------
//类型常量定义
//----------------------
constAXML_FILE=0x00080003;
constSTRING_BLOCK=0x001C0001;
constRESOURCEIDS=0x00080180;
constSTART_NAMESPACE=0x00100100;
constEND_NAMESPACE=0x00100101;
constSTART_TAG=0x00100102;
constEND_TAG=0x00100103;
constTEXT=0x00100104;
constTYPE_NULL=0;
constTYPE_REFERENCE=1;
constTYPE_ATTRIBUTE=2;
constTYPE_STRING=3;
constTYPE_FLOAT=4;
constTYPE_DIMENSION=5;
constTYPE_FRACTION=6;
constTYPE_INT_DEC=16;
constTYPE_INT_HEX=17;
constTYPE_INT_BOOLEAN=18;
constTYPE_INT_COLOR_ARGB8=28;
constTYPE_INT_COLOR_RGB8=29;
constTYPE_INT_COLOR_ARGB4=30;
constTYPE_INT_COLOR_RGB4=31;
constUNIT_MASK=15;
privatestatic$RADIX_MULTS=array(0.00390625,3.051758E-005,1.192093E-007,4.656613E-010);
privatestatic$DIMENSION_UNITS=array("px","dip","sp","pt","in","mm","","");
privatestatic$FRACTION_UNITS=array("%","%p","","","","","","");
private$xml='';
private$length=0;
private$stringCount=0;
private$styleCount=0;
private$stringTab=array();
private$styleTab=array();
private$resourceIDs=array();
private$ns=array();
private$cur_ns=NULL;
private$root=NULL;
private$line=0;
//----------------------
//内部私有函数
//----------------------
privatefunctiongetElement($path){
if(!$this->root)returnNULL;
$ps=explode('/',$path);
$r=$this->root;
foreach($psas$v){
if(preg_match('/([^\[]+)\[([0-9]+)\]$/',$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;continue2;
}else{
$off--;
}
}
}
//没有找到节点
returnNULL;
}
return$r;
}
privatefunctionparseBlock($need=0){
$o=0;
$type=$this->get32($o);
if($need&&$type!=$need)thrownewException('BlockTypeError',1);
$size=$this->get32($o);
if($size<8||$size>$this->length)thrownewException('BlockSizeError',2);
$left=$this->length-$size;
$props=false;
switch($type){
caseself::AXML_FILE:
$props=array(
'line'=>0,
'tag'=>'<?xmlversion="1.0"encoding="utf-8"?>'
);
break;
caseself::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):array();
$this->styleTab=$this->styleCount>0?$this->getStringTab($styOffset,$styListOffset):array();
$o=$size;
break;
caseself::RESOURCEIDS:
$count=$size/4-2;
$this->resourceIDs=$this->get32array($o,$count);
break;
caseself::START_NAMESPACE:
$o+=8;
$prefix=$this->get32($o);
$uri=$this->get32($o);
if(empty($this->cur_ns)){
$this->cur_ns=array();
$this->ns[]=&$this->cur_ns;
}
$this->cur_ns[$uri]=$prefix;
break;
caseself::END_NAMESPACE:
$o+=8;
$prefix=$this->get32($o);
$uri=$this->get32($o);
if(empty($this->cur_ns))break;
unset($this->cur_ns[$uri]);
break;
caseself::START_TAG:
$line=$this->get32($o);
$o+=4;
$attrs=array();
$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_nsas$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=array();
$this->ns[]=&$this->cur_ns;
$left=-1;
break;
caseself::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;
caseself::TEXT:
$o+=8;
$props=array(
'tag'=>$this->getString($this->get32($o))
);
$o+=8;
break;
default:
thrownewException('BlockTypeError',3);
break;
}
$this->skip($o);
$child=array();
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)thrownewException('BlockOverflowError',4);
if($props){
$props['type']=$type;
$props['size']=$size;
$props['child']=$child;
return$props;
}else{
returnfalse;
}
}
privatefunctiongetAttributeValue($a){
$type=&$a['val_type'];
$data=&$a['val_data'];
switch($type){
caseself::TYPE_STRING:
return$this->getString($a['val_str']);
caseself::TYPE_ATTRIBUTE:
returnsprintf('?%s%08X',self::_getPackage($data),$data);
caseself::TYPE_REFERENCE:
returnsprintf('@%s%08X',self::_getPackage($data),$data);
caseself::TYPE_INT_HEX:
returnsprintf('0x%08X',$data);
caseself::TYPE_INT_BOOLEAN:
return($data!=0?'true':'false');
caseself::TYPE_INT_COLOR_ARGB8:
caseself::TYPE_INT_COLOR_RGB8:
caseself::TYPE_INT_COLOR_ARGB4:
caseself::TYPE_INT_COLOR_RGB4:
returnsprintf('#%08X',$data);
caseself::TYPE_DIMENSION:
return$this->_complexToFloat($data).self::$DIMENSION_UNITS[$data&self::UNIT_MASK];
caseself::TYPE_FRACTION:
return$this->_complexToFloat($data).self::$FRACTION_UNITS[$data&self::UNIT_MASK];
caseself::TYPE_FLOAT:
return$this->_int2float($data);
}
if($type>=self::TYPE_INT_DEC&&$type<self::TYPE_INT_COLOR_ARGB8){
return(string)$data;
}
returnsprintf('<0x%X,type0x%02X>',$data,$type);
}
privatefunction_complexToFloat($data){
return(float)($data&0xFFFFFF00)*self::$RADIX_MULTS[($data>>4)&3];
}
privatefunction_int2float($v){
$x=($v&((1<<23)-1))+(1<<23)*($v>>31|1);
$exp=($v>>23&0xFF)-127;
return$x*pow(2,$exp-23);
}
privatestaticfunction_getPackage($data){
return($data>>24==1)?'android:':'';
}
privatefunctiongetStringTab($base,$list){
$tab=array();
foreach($listas$off){
$off+=$base;
$len=$this->get16($off);
$mask=($len>>0x8)&0xFF;
$len=$len&0xFF;
if($len==$mask){
if($off+$len>$this->length)thrownewException('StringTableOverflow',11);
$tab[]=substr($this->xml,$off,$len);
}else{
if($off+$len*2>$this->length)thrownewException('StringTableOverflow',11);
$str=substr($this->xml,$off,$len*2);
$tab[]=mb_convert_encoding($str,'UTF-8','UCS-2LE');
}
}
return$tab;
}
privatefunctiongetString($id){
if($id>-1&&$id<$this->stringCount){
return$this->stringTab[$id];
}else{
return'';
}
}
privatefunctiongetNameSpace($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'';
}
privatefunctionget32(&$off){
$int=unpack('V',substr($this->xml,$off,4));
$off+=4;
returnarray_shift($int);
}
privatefunctionget32array(&$off,$size){
if($size<=0)returnNULL;
$arr=unpack('V*',substr($this->xml,$off,4*$size));
if(count($arr)!=$size)thrownewException('ArraySizeError',10);
$off+=4*$size;
return$arr;
}
privatefunctionget16(&$off){
$int=unpack('v',substr($this->xml,$off,2));
$off+=2;
returnarray_shift($int);
}
privatefunctionskip($size){
$this->xml=substr($this->xml,$size);
$this->length-=$size;
}
}
//---------------------
//Apkparser类包结束
//---------------------
?>
感兴趣的朋友可以调试运行一下本文实例,相信会对大家的php程序开发带来一定的启发。