有时候在使用php上传安卓apk包的时候,我们需要获取安卓apk包内的信息,本文以实例形式讲述了php获取apk包信息的方法。具体实现方法如下:
<?php /*解析安卓apk包中的压缩XML文件,还原和读取XML内容 依赖功能:需要PHP的ZIP包函数支持。*/ include(\'./Apkparser.php\'); $appObj = new Apkparser(); $targetFile = a.apk;//apk所在的路径地址 $res = $appObj->open($targetFile); $appObj->getAppName(); // 应用名称 $appObj->getPackage(); // 应用包名 $appObj->getVersionName(); // 版本名称 $appObj->getVersionCode(); // 版本代码 ?>
以下是Apkparser类包,把以下代码复制出来保存为Apkparser.php就可以执行以上代码
<?php
//-------------------------------
//Apkparser类包开始
//-------------------------------
class ApkParser{
//----------------------
// 公共函数,供外部调用
//----------------------
public function open($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){
}
}
}
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;
}
//----------------------
// 类型常量定义
//----------------------
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 $RADIX_MULTS = array(0.00390625, 3.051758E-005, 1.192093E-007, 4.656613E-010);
private static $DIMENSION_UNITS = array(\"px\",\"dip\",\"sp\",\"pt\",\"in\",\"mm\",\"\",\"\");
private static $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;
//----------------------
// 内部私有函数
//----------------------
private function getElement($path){
if (!$this->root) return NULL;
$ps = explode(\'/\', $path);
$r = $this->root;
foreach ($ps as $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; 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 = array(
\'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) : array();
$this->styleTab = $this->styleCount > 0 ? $this->getStringTab($styOffset, $styListOffset) : array();
$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 = array();
$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 = 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_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 = array();
$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 = 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) 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::$DIMENSION_UNITS[$data & self::UNIT_MASK];
case self::TYPE_FRACTION:
return $this->_complexToFloat($data).self::$FRACTION_UNITS[$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::$RADIX_MULTS[($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 = array();
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;
}
}
//---------------------
//Apkparser类包结束
//---------------------
?>
感兴趣的朋友可以调试运行一下本文实例,相信会对大家的php程序开发带来一定的启发。
本文地址:https://www.stayed.cn/item/18306
转载请注明出处。
本站部分内容来源于网络,如侵犯到您的权益,请 联系我