有时候在使用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程序开发带来一定的启发。
评论列表:
发布于 3天前回复该评论
发布于 3天前回复该评论
发布于 3天前回复该评论
发布于 3天前回复该评论
发布于 2天前回复该评论
发布于 2天前回复该评论
发布于 2天前回复该评论
发布于 2天前回复该评论