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程序开发带来一定的启发。