php获取apk包信息的方法
原文地址:http://www.jb51.net/article/53780.htm
这篇文章主要介绍了php获取apk包信息的方法,非常实用的功能,需要的朋友可以参考下
有时候在使用php上传安卓apk包的时候,我们需要获取安卓apk包内的信息,本文以实例形式讲述了php获取apk包信息的方法。具体实现方法如下:
?
1
2
3
4
5
6
7
8
9
10
11
12
/*解析安卓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就可以执行以上代码
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
//-------------------------------
//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 $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' => ''
);
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 $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 = " 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 return (string)$data;
}
return sprintf('', $data, $type);
}
private function _complexToFloat($data){
return (float)($data & 0xFFFFFF00) * self::$RADIX_MULTS[($data>>4) & 3];
}
private function _int2float($v) {
$x = ($v & ((1 > 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 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 $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程序开发带来一定的启发。
0
0
相关文章
php中nbsp是什么意思
如何从PHP中调用Python文件?
我们如何使用使用mysql_fetch_assoc()函数的PHP脚本来显示MySQL表中的所有记录?
ini_set()在PHP中的作用是什么?
在PHP中的readlink()函数
本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
热门AI工具
相关专题
本专题系统整理pixiv网页版官网入口及登录访问方式,涵盖官网登录页面直达路径、在线阅读入口及快速进入方法说明,帮助用户高效找到pixiv官方网站,实现便捷、安全的网页端浏览与账号登录体验。
705
2026.02.13
本专题系统整理微博网页版官方入口及网页端登录方式,涵盖首页直达地址、账号登录流程与常见访问问题说明,帮助用户快速找到微博官网主页,实现便捷、安全的网页端登录与内容浏览体验。
233
2026.02.13
本专题围绕Flutter框架展开,系统讲解跨平台UI构建原理与状态管理方案。内容涵盖Widget生命周期、路由管理、Provider与Bloc状态管理模式、网络请求封装及性能优化技巧。通过实战项目演示,帮助开发者构建流畅、可维护的跨平台移动应用。
117
2026.02.13
本专题面向前端开发者,深入讲解 TypeScript 类型系统与大型项目结构设计方法,并结合 Vite 构建工具优化前端工程化流程。内容包括模块化设计、类型声明管理、代码分割、热更新原理以及构建性能调优。通过完整项目示例,帮助开发者提升代码可维护性与开发效率。
22
2026.02.13
本专题围绕 Redis 在高并发系统中的应用展开,系统讲解主从复制、哨兵机制、Cluster 集群模式及数据分片原理。内容涵盖缓存穿透与雪崩解决方案、分布式锁实现、热点数据优化及持久化策略。通过真实业务场景演示,帮助开发者构建高可用、可扩展的分布式缓存系统。
61
2026.02.13
本专题系统整理雨课堂网页版官方入口及在线登录方式,涵盖账号登录流程、官方直连入口及平台访问方法说明,帮助师生用户快速进入雨课堂在线教学平台,实现便捷、高效的课程学习与教学管理体验。
15
2026.02.12
本专题汇总豆包AI官方网页版入口及在线使用方式,涵盖智能写作工具、图片生成体验入口和官网登录方法,帮助用户快速直达豆包AI平台,高效完成文本创作与AI生图任务,实现便捷智能创作体验。
669
2026.02.12
本专题面向后端开发与数据库工程师,深入讲解 PostgreSQL 查询优化原理与索引机制。内容包括执行计划分析、常见索引类型对比、慢查询优化策略、事务隔离级别以及高并发场景下的性能调优技巧。通过实战案例解析,帮助开发者提升数据库响应速度与系统稳定性。
58
2026.02.12
热门下载
相关下载
精品课程
最新文章


