php获取apk包信息的方法
程序员文章站
2022-06-02 09:21:02
有时候在使用php上传安卓apk包的时候,我们需要获取安卓apk包内的信息,本文以实例形式讲述了php获取apk包信息的方法。具体实现方法如下:
...
有时候在使用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程序开发带来一定的启发。