CentOS6编译安装Apache2.4 & PHP5.6

引子

作为一个Web开发者, 编译php是看家本领, 而目前互联网上的各种资料皆无法一次搞定编译安装, 故有此文.
本文安装环境是CentOS6.5 64位版本

1
2
3
# cat /proc/version
Linux version 2.6.32-431.el6.x86_64 (mockbuild@c6b8.bsys.dev.centos.org) (gcc version 4.4.7 20120313 (Red Hat 4.4.7-4) (GCC) ) #1 SMP Fri Nov 22 03:15:09 UTC 2013

确保已经安装编译器! (吐槽一下yum安装的gcc还是4.4的版本, Ubuntu都已经是4.8了)

1
2
3
yum install gcc
yum install make
yum install gcc-c++

懒人专用

1
2
3
4
5
yum install -y libxml2 libxml2-devel openssl openssl-devel bzip2 bzip2-devel curl curl-devel libpng-devel libmcrypt-devel

wget "http://mirrors.aliyun.com/apache/httpd/httpd-2.4.20.tar.gz" "http://mirrors.aliyun.com/apache/httpd/httpd-2.4.20-deps.tar.gz" "ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/pcre-8.39.tar.gz" "http://cn2.php.net/distributions/php-5.6.22.tar.gz"

for tar in *.tar.gz; do tar -zxvf $tar; done

编译安装Apache

寻思躲懒, 使用yum方式安装Apache, 结果在编译php的时候无法生成libphp5.so文件, 无奈只得老老实实编译.
首先下载源码
http://mirrors.aliyun.com/apache/httpd/httpd-2.4.20.tar.gz
http://mirrors.aliyun.com/apache/httpd/httpd-2.4.20-deps.tar.gz
ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/pcre-8.39.tar.gz
解压之后开始编译(httpd-2.4.20.tar.gz 和 httpd-2.4.20-deps.tar.gz 放到同一个文件夹下)

1
2
3
4
5
6
7
8
9
// pcre

./configure --prefix=/usr/local/pcre
make && make install

// apache

./configure --with-pcre=/usr/local/pcre --enable-so
make && make install

若没有报错, 则表示编译安装成功, 值得注意的是, 此时的默认wwwroot还是编译路径下的htcdocs目录, 需要手动修改到常用的/var/www/html

DocumentRoot "/var/www/html"

此时在/var/www/html目录下新建index.html并写入内容, 在浏览器访问服务器ip能显示写入内容的情况下, 可以判定Apache编译安装成功.

编译安装PHP

重头戏来了, 目前网络上绝大多数关于编译PHP的资料, 都或多或少的有坑, 不能一次成功, 在这篇文章中, 我总结了所有遇到的错误和解决方法, 直接贴上原始命令. 当然首先是下载PHP的源码了, 这里就不再赘述.
http://cn2.php.net/distributions/php-5.6.22.tar.gz

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
./configure --prefix=/usr/local/php --with-apxs2=/usr/local/apache2/bin/apxs --with-config-file-path=/usr/local/php/etc --with-mysql=mysqlnd --with-mysqli=mysqlnd --with-pdo-mysql=mysqlnd --with-gettext --enable-mbstring --enable-exif --with-iconv --with-mcrypt --with-mhash --with-openssl --enable-bcmath --enable-soap --with-libxml-dir --enable-pcntl --enable-shmop --enable-sysvmsg --enable-sysvsem --enable-sysvshm --enable-sockets --with-curl --with-gd --with-zlib --enable-zip --with-bz2  --without-sqlite3 --without-pdo-sqlite --with-pear --enable-opcache

make && make install

1.
Q:
configure: error: xml2-config not found. Please check your libxml2 installation.

A:
yum install libxml2
yum install libxml2-devel

2.
Q:
configure: error: Cannot find OpenSSL's libraries
configure: error: Cannot find OpenSSL's <evp.h>
A:
yum install openssl
yum install openssl-devel
如果继续报错(一般原版centOS不会有这个问题):
find / -name libssl.so
/usr/lib/x86_64-linux-gnu/libssl.so
初步判断它可能只会在 /usr/lib/ 下寻找 libssl.so 文件, 于是:
ln -s /usr/lib/x86_64-linux-gnu/libssl.so /usr/lib
重新编译安装即通过.

3.
Q:
configure: error: Please reinstall the BZip2 distribution

A:
yum install bzip2
yum install bzip2-devel

4.
Q:
configure: error: Please reinstall the libcurl distribution -
easy.h should be in <curl-dir>/include/curl/

A:
yum install curl
yum install curl-devel

5.
Q:
configure: error: png.h not found.

A:
yum install libpng-devel

6.
Q:
configure: error: mcrypt.h not found. Please reinstall libmcrypt.

A:
yum install libmcrypt-devel

暂时只遇到这么多问题, 后续有问题会继续补充.

Apache与PHP的整合

其实做到这里已经差不多完成了, 只需要把二者进行整合, 直接在httpd.conf中添加:

1
2
3
<FilesMatch \.php$>
SetHandler application/x-httpd-php
</FilesMatch>

另外需要检测index.php则需要找到这一行并且在后面追加:

DirectoryIndex index.html index.php

接着重启Apache, 完成.

MySQL相关

使用apt-get install mysql-server 之后, 发觉mysql_connect()这类函数会报错, 提示

mysql_connect(): [2002] No such file or directory

这是表示没有找到mysql.sock文件, 只需去mysql的my.cnf中找一下真实路径, 然后做一下软连接即可, 也可能是mysqld.sock.

ln -s /var/run/mysqld/mysqld.sock /tmp/mysql.sock

另外这里注意一下centOS6的一个坑
yum安装的mysql是5.1版本的, 所以只能使用phpMyAdmin4.0系列版本才行:
https://files.phpmyadmin.net/phpMyAdmin/4.0.10.15/phpMyAdmin-4.0.10.15-all-languages.tar.gz
这个是4.6版本, 仅支持5.5以及以上的mysql:
https://files.phpmyadmin.net/phpMyAdmin/4.6.2/phpMyAdmin-4.6.2-all-languages.tar.gz

后记

CentOS有一个小坑在防火墙这块, 导致网页无法访问

1
2
关闭命令: service iptables stop 
永久关闭防火墙: chkconfig iptables off

整个编译过程加上资料查阅差不多花费了近2小时, 主要是一些Linux的基本命令不熟悉, 大部分时间消耗在查询基本命令的文档上面了, 以后要多注意这方面~

参考资料

http://www.2cto.com/os/201404/294000.html
http://blog.csdn.net/tianguokaka/article/details/19086789
http://my.oschina.net/megan/blog/325040
http://blog.knowsky.com/192286.htm

Ubuntu14.04 & CentOS6.5 编译安装Apache & PHP5.6

引子

作为一个Web开发者, 编译php是看家本领, 而目前互联网上的各种资料皆无法一次搞定编译安装, 故有此文.
本文安装环境是Ubuntu14.04 64位版本 & CentOS6.5 64位版本

确保已经安装编译器!

1
2
3
apt-get install gcc
apt-get install make
apt-get install build-essential
1
2
3
yum install gcc
yum install make
yum install gcc-c++

懒人专用

1
2
3
4
apt-get update
apt-get install gcc make build-essential libxml2 libxml2-dev openssl bzip2 libbz2-dev curl libpng12-dev libmcrypt-dev -y
apt-get install libcurl4-gnutls-dev -y
apt-get install libcurl4-openssl-dev -y
1
yum install -y libxml2 libxml2-devel openssl openssl-devel bzip2 bzip2-devel curl curl-devel libpng-devel libmcrypt-devel
1
2
3
wget "http://mirrors.aliyun.com/apache/httpd/httpd-2.4.20.tar.gz" "http://mirrors.aliyun.com/apache/httpd/httpd-2.4.20-deps.tar.gz" "ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/pcre-8.39.tar.gz" "http://cn2.php.net/distributions/php-5.6.23.tar.gz"

for tar in *.tar.gz; do tar -zxvf $tar; done

编译安装Apache

首先下载源码

http://mirrors.aliyun.com/apache/httpd/httpd-2.4.20.tar.gz
http://mirrors.aliyun.com/apache/httpd/httpd-2.4.20-deps.tar.gz
ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/pcre-8.39.tar.gz

解压之后开始编译

1
2
3
4
5
6
7
# pcre
./configure --prefix=/usr/local/pcre
make && make install

# httpd
./configure --enable-so --with-pcre=/usr/local/pcre
make && make install

若没有报错, 则表示编译安装成功, 值得注意的是, 此时的默认wwwroot还是编译路径下的htcdocs目录, 需要手动修改到常用的/var/www/html

DocumentRoot "/var/www/html"

此时在/var/www/html目录下新建index.html并写入内容, 在浏览器访问服务器ip能显示写入内容的情况下, 可以判定Apache编译安装成功.

编译安装PHP

重头戏来了, 目前网络上绝大多数关于编译PHP的资料, 都或多或少的有坑, 不能一次成功, 在这篇文章中, 我总结了所有遇到的错误和解决方法, 直接贴上原始命令. 当然首先是下载PHP的源码了, 这里就不再赘述.
http://cn2.php.net/distributions/php-5.6.23.tar.gz

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
./configure --prefix=/usr/local/php --with-apxs2=/usr/local/apache2/bin/apxs --with-config-file-path=/usr/local/php/etc --with-mysql=mysqlnd --with-mysqli=mysqlnd --with-pdo-mysql=mysqlnd --with-gettext --enable-mbstring --enable-exif --with-iconv --with-mcrypt --with-mhash --with-openssl --enable-bcmath --enable-soap --with-libxml-dir --enable-pcntl --enable-shmop --enable-sysvmsg --enable-sysvsem --enable-sysvshm --enable-sockets --with-curl --with-gd --with-zlib --enable-zip --with-bz2  --without-sqlite3 --without-pdo-sqlite --with-pear --enable-opcache

make && make install

1.
Q:
configure: error: xml2-config not found. Please check your libxml2 installation.

A:
apt-get install libxml2
apt-get install libxml2-dev

yum install libxml2
yum install libxml2-devel

2.
Q:
configure: error: Cannot find OpenSSL's libraries

A:
apt-get install openssl
如果继续报错:
find / -name libssl.so
/usr/lib/x86_64-linux-gnu/libssl.so
初步判断它可能只会在 /usr/lib/ 下寻找 libssl.so 文件, 于是:
ln -s /usr/lib/x86_64-linux-gnu/libssl.so /usr/lib
重新编译安装即通过.

yum install openssl
yum install openssl-devel

3.
Q:
configure: error: Please reinstall the BZip2 distribution

A:
apt-get install bzip2
apt-get install libbz2-dev

yum install bzip2
yum install bzip2-devel

4.
Q:
configure: error: Please reinstall the libcurl distribution -
easy.h should be in <curl-dir>/include/curl/

A:
apt-get install curl
apt-get install libcurl4-gnutls-dev

yum install curl
yum install curl-devel

5.
Q:
configure: error: png.h not found.

A:
apt-get install libpng12-dev

yum install libpng-devel

6.
Q:
configure: error: mcrypt.h not found. Please reinstall libmcrypt.

A:
apt-get install libmcrypt-dev

yum install libmcrypt-devel

7.
Q:
configure: error: Cannot find OpenSSL's <evp.h>

A:
apt-get install libcurl3-openssl-dev

暂时只遇到这么多问题, 后续有问题会继续补充.

Apache与PHP的整合

其实做到这里已经差不多完成了, 只需要把二者进行整合, 直接在httpd.conf中添加:

1
2
3
<FilesMatch \.php$>
SetHandler application/x-httpd-php
</FilesMatch>

另外需要检测index.php则需要找到这一行并且在后面追加:

DirectoryIndex index.html index.php

接着重启Apache, 完成.

MySQL相关

使用apt-get install mysql-server 之后, 发觉mysql_connect()这类函数会报错, 提示

mysql_connect(): [2002] No such file or directory

这是表示没有找到mysql.sock文件, 只需去mysql的my.cnf中找一下真实路径, 然后做一下软连接即可, 也可能是mysqld.sock.

ln -s /var/run/mysqld/mysqld.sock /tmp/mysql.sock

另外这里注意一下centOS6的一个坑
yum安装的mysql是5.1版本的, 所以只能使用phpMyAdmin4.0系列版本才行:
https://files.phpmyadmin.net/phpMyAdmin/4.0.10.15/phpMyAdmin-4.0.10.15-all-languages.tar.gz
这个是4.6版本, 仅支持5.5以及以上的mysql:
https://files.phpmyadmin.net/phpMyAdmin/4.6.3/phpMyAdmin-4.6.3-all-languages.tar.gz

后记

CentOS有一个小坑在防火墙这块, 导致网页无法访问

1
2
关闭命令: service iptables stop 
永久关闭防火墙: chkconfig iptables off

整个编译过程加上资料查阅差不多花费了近2小时, 主要是一些Linux的基本命令不熟悉, 大部分时间消耗在查询基本命令的文档上面了, 以后要多注意这方面~

参考资料

http://www.2cto.com/os/201404/294000.html
http://blog.csdn.net/tianguokaka/article/details/19086789
http://my.oschina.net/megan/blog/325040
http://blog.knowsky.com/192286.htm

Ubuntu下Apache配置SSL笔记

引言

本来以为这类教程很成熟的照搬就可以用的, 但是实际操作之后还是有些许问题, 故有此文, 本文暂且不讨论各个文件的作用, 只单纯讨论配制方法.

步骤

首先需要在主机上生成CSR文件和KEY文件, 这里需要注意的是
common name 需要填写域名, 如果不是wildcard认证需要带上www前缀.
生成完毕就可以发给认证机构来进行认证了.
认证成功后机构会发给你这么几个文件(我在COMODO认证的)

1
2
3
4
AddTrustExternalCARoot.crt
COMODORSAAddTrustCA.crt
COMODORSADomainValidationSecureServerCA.crt
你的域名.crt

首先需要合并crt文件:

# cat COMODORSADomainValidationSecureServerCA.crt COMODORSAAddTrustCA.crt AddTrustExternalCARoot.crt > bundle.crt

用下面的命令确保ssl模块已经加载进apache了:

# a2enmod ssl

如果你看到了“Module ssl already enabled”这样的信息就说明你成功了,如果你看到了“Enabling module ssl”,那么你还需要用下面的命令重启apache:

# service apache2 restart

另外, 如果你是编译安装的Apache, 则需要在httpd.conf中去掉mod_ssl的注释以及mod_socache_shmcb的注释, 另外还要在extra目录下编辑httpd-ssl.conf, 这些都是和用apt-get方式安装所不同的地方


到了这里就是关键的部分了, 网上的教程大多不适合Ubuntu下的Apache配置, 我来总结一下我自己的经验.

首先配置Apache的ssl配置文件:

# vim /etc/apache2/sites-available/default-ssl.conf

找到

1
2
3
SSLCertificateFile /path/to/crt-file
SSLCertificateKeyFile /path/to/key-file
SSLCACertificateFile /path/to/bundle.crt-file

按需修改之后, 再进行软连接 (如果是编译安装则是去httpd-ssl.conf中去掉include httpd-ssl.conf的注释)

# ln -s /etc/apache2/sites-available/default-ssl.conf /etc/apache2/sites-enabled/default-ssl.conf 

大功告成, restart一下Apache, 尽情享用吧~

Apache 使用rewrite模块强制走 https 配置:
.htaccess文件:

1
2
3
RewriteEngineOn
RewriteCond %{SERVER_PORT} 80
RewriteRule ^(.*)?$ https://%{SERVER_NAME}/$1 [R=301,L]

参考资料

http://www.linuxidc.com/Linux/2015-02/113588.htm

Web性能优化之Apache篇

引言

本篇为Apache服务器的性能优化笔记, 记录了优化的点滴.

HSTS策略

引言

一直以来, 实现强制https的方法是使用Apache的rewrite模块来进行重定向, 这样存在几个问题, 第一是性能问题, 第二是可能遇到不支持https的客户端…..等等

核心思想

避免这种跳转, 我们可以用HSTS策略, 就是告诉浏览器, 以后访问我这个站点, 必须用HTTPS协议来访问, 让浏览器帮忙做转换, 而不是请求到了服务器后, 才知道要转换. 只需要在响应头部加上 Strict-Transport-Security: max-age=31536000 即可.

在centos上实践:

1
2
3
4
5
6
7
8
<VirtualHost *:80>
ServerAdmin admin@admin.com
DocumentRoot /var/www/html
ServerName admin.com
ErrorLog logs/admin.log
CustomLog logs/admin.log common
Header always set Strict-Transport-Security "max-age=63072000; includeSubdomains; preload"
</VirtualHost>

小结

这种方法也有一定局限性: 不是所有浏览器都支持这个http头

参考资料

http://linux-audit.com/configure-hsts-http-strict-transport-security-apache-nginx/
https://raymii.org/s/tutorials/HTTP_Strict_Transport_Security_for_Apache_NGINX_and_Lighttpd.html

HTTP持久连接

引言

HTTP持久连接可以重用已建立的TCP连接,减少三次握手的RTT延迟。浏览器在请求时带上 connection: keep-alive 的头部,服务器收到后就要发送完响应后保持连接一段时间,浏览器在下一次对该服务器的请求时,就可以直接拿来用。

在centos上实践:

直接编辑httpd.conf, 设置 KeepAlive 参数为 On

小结

以往, 浏览器判断响应数据是否接收完毕, 是看连接是否关闭. 在使用持久连接后, 就不能这样了, 这就要求服务器对持久连接的响应头部一定要返回 content-length 标识body的长度, 供浏览器判断界限. 有时, content-length 的方法并不是太准确, 也可以使用 Transfer-Encoding: chunked 头部发送一串一串的数据, 最后由长度为0的chunked标识结束.

VIM笔记本

本篇用于记录使用VIM的点滴


快捷命令

复制 yy
粘贴 p
定位到顶部 gg
定位到底部 G


命令行命令

q! 不保存更改并退出vim

PHP读取docx文档内容

引言

客户需求, 需要从docx文档读取内容并且做简单格式化, 难点就在于如何读取docx格式并且转换为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
124
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
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
/**
* Class Docx2Text
*
* Docx => String
*/
class Docx2Text
{
const SEPARATOR_TAB = "\t";

/**
* object zipArchive
*
* @var string
* @access private
*/
private $docx;

/**
* object domDocument from document.xml
*
* @var string
* @access private
*/
private $domDocument;

/**
* xml from document.xml
*
* @var string
* @access private
*/
private $_document;

/**
* xml from numbering.xml
*
* @var string
* @access private
*/
private $_numbering;

/**
* xml from footnote
*
* @var string
* @access private
*/
private $_footnote;

/**
* xml from endnote
*
* @var string
* @access private
*/
private $_endnote;

/**
* array of all the endnotes of the document
*
* @var string
* @access private
*/
private $endnotes;

/**
* array of all the footnotes of the document
*
* @var string
* @access private
*/
private $footnotes;

/**
* array of all the relations of the document
*
* @var string
* @access private
*/
private $relations;

/**
* array of characters to insert like a list
*
* @var string
* @access private
*/
private $numberingList;

/**
* the text content that will be exported
*
* @var string
* @access private
*/
private $textOuput;


/**
* boolean variable to know if a chart will be transformed to text
*
* @var string
* @access private
*/
private $chart2text;

/**
* boolean variable to know if a table will be transformed to text
*
* @var string
* @access private
*/
private $table2text;

/**
* boolean variable to know if a list will be transformed to text
*
* @var string
* @access private
*/
private $list2text;

/**
* boolean variable to know if a paragraph will be transformed to text
*
* @var string
* @access private
*/
private $paragraph2text;

/**
* boolean variable to know if footnotes will be extracteded
*
* @var string
* @access private
*/
private $footnote2text;

/**
* boolean variable to know if endnotes will be extracted
*
* @var string
* @access private
*/
private $endnote2text;

/**
* Construct
*
* @param $boolTransforms array of boolean values of which elements should be transformed or not
* @access public
*/

public function __construct($boolTransforms = array())
{
//table,list, paragraph, footnote, endnote, chart
if (isset($boolTransforms['table'])) {
$this->table2text = $boolTransforms['table'];
} else {
$this->table2text = true;
}

if (isset($boolTransforms['list'])) {
$this->list2text = $boolTransforms['list'];
} else {
$this->list2text = true;
}

if (isset($boolTransforms['paragraph'])) {
$this->paragraph2text = $boolTransforms['paragraph'];
} else {
$this->paragraph2text = true;
}

if (isset($boolTransforms['footnote'])) {
$this->footnote2text = $boolTransforms['footnote'];
} else {
$this->footnote2text = true;
}

if (isset($boolTransforms['endnote'])) {
$this->endnote2text = $boolTransforms['endnote'];
} else {
$this->endnote2text = true;
}

if (isset($boolTransforms['chart'])) {
$this->chart2text = $boolTransforms['chart'];
} else {
$this->chart2text = true;
}

$this->textOuput = '';
$this->docx = null;
$this->_numbering = '';
$this->numberingList = array();
$this->endnotes = array();
$this->footnotes = array();
$this->relations = array();

}

/**
*
* Extract the content of a word document and create a text file if the name is given
*
* @access public
* @param string $filename of the word document.
*
* @return string
*/

public function extract($filename = '')
{
if (empty($this->_document)) {
//xml content from document.xml is not got
exit('There is no content');
}

$this->domDocument = new DomDocument();
$this->domDocument->loadXML($this->_document);
//get the body node to check the content from all his children
$bodyNode = $this->domDocument->getElementsByTagNameNS('http://schemas.openxmlformats.org/wordprocessingml/2006/main', 'body');
//We get the body node. it is known that there is only one body tag
$bodyNode = $bodyNode->item(0);
foreach ($bodyNode->childNodes as $child) {
//the children can be a table, a paragraph or a section. We only implement the 2 first option said.
if ($this->table2text && $child->tagName == 'w:tbl') {
//this node is a table and the content is split with tabs if the variable table2text from the class is true
$this->textOuput .= $this->table($child) . $this->separator();
} else {
//this node is a paragraph
$this->textOuput .= $this->printWP($child) . ($this->paragraph2text ? $this->separator() : '');
}
}
if (!empty($filename)) {
$this->writeFile($filename, $this->textOuput);
} else {
return $this->textOuput;
}
}

/**
* Setter
*
* @access public
* @param $filename
*/
public function setDocx($filename)
{
$this->docx = new ZipArchive();
$ret = $this->docx->open($filename);
if ($ret === true) {
$this->_document = $this->docx->getFromName('word/document.xml');
} else {
exit('failed');
}
}

/**
* extract the content to an array from endnote.xml
*
* @access private
*/
private function loadEndNote()
{
if (empty($this->endnotes)) {
if (empty($this->_endnote)) {
$this->_endnote = $this->docx->getFromName('word/endnotes.xml');
}
if (!empty($this->_endnote)) {
$domDocument = new DomDocument();
$domDocument->loadXML($this->_endnote);
$endnotes = $domDocument->getElementsByTagNameNS('http://schemas.openxmlformats.org/wordprocessingml/2006/main', 'endnote');
foreach ($endnotes as $endnote) {
$xml = $endnote->ownerDocument->saveXML($endnote);
$this->endnotes[$endnote->getAttribute('w:id')] = trim(strip_tags($xml));
}
}
}
}

/**
* Extract the content to an array from footnote.xml
*
* @access private
*/
private function loadFootNote()
{
if (empty($this->footnotes)) {
if (empty($this->_footnote)) {
$this->_footnote = $this->docx->getFromName('word/footnotes.xml');
}
if (!empty($this->_footnote)) {
$domDocument = new DomDocument();
$domDocument->loadXML($this->_footnote);
$footnotes = $domDocument->getElementsByTagNameNS('http://schemas.openxmlformats.org/wordprocessingml/2006/main', 'footnote');
foreach ($footnotes as $footnote) {
$xml = $footnote->ownerDocument->saveXML($footnote);
$this->footnotes[$footnote->getAttribute('w:id')] = trim(strip_tags($xml));
}
}
}
}

/**
* Extract the styles of the list to an array
*
* @access private
*/
private function listNumbering()
{
$ids = array();
$nums = array();
//get the xml code from the zip archive
$this->_numbering = $this->docx->getFromName('word/numbering.xml');
if (!empty($this->_numbering)) {
//we use the domdocument to iterate the children of the numbering tag
$domDocument = new DomDocument();
$domDocument->loadXML($this->_numbering);
$numberings = $domDocument->getElementsByTagNameNS('http://schemas.openxmlformats.org/wordprocessingml/2006/main', 'numbering');
//there is only one numbering tag in the numbering.xml
$numberings = $numberings->item(0);
foreach ($numberings->childNodes as $child) {
$flag = true;//boolean variable to know if the node is the first style of the list
foreach ($child->childNodes as $son) {
if ($child->tagName == 'w:abstractNum' && $son->tagName == 'w:lvl') {
foreach ($son->childNodes as $daughter) {
if ($daughter->tagName == 'w:numFmt' && $flag) {
$nums[$child->getAttribute('w:abstractNumId')] = $daughter->getAttribute('w:val');//set the key with internal index for the listand the value it is the type of bullet
$flag = false;
}
}
} elseif ($child->tagName == 'w:num' && $son->tagName == 'w:abstractNumId') {
$ids[$son->getAttribute('w:val')] = $child->getAttribute('w:numId');//$ids is the index of the list
}
}
}
//once we know what kind of list there is in the documents, is prepared the bullet that the library will use
foreach ($ids as $ind => $id) {
if ($nums[$ind] == 'decimal') {
//if the type is decimal it means that the bullet will be numbers
$this->numberingList[$id][0] = range(1, 10);
$this->numberingList[$id][1] = range(1, 10);
$this->numberingList[$id][2] = range(1, 10);
$this->numberingList[$id][3] = range(1, 10);
} else {
//otherwise is *, and other characters
$this->numberingList[$id][0] = array('*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*');
$this->numberingList[$id][1] = array(chr(175), chr(175), chr(175), chr(175), chr(175), chr(175), chr(175), chr(175), chr(175), chr(175), chr(175), chr(175));
$this->numberingList[$id][2] = array(chr(237), chr(237), chr(237), chr(237), chr(237), chr(237), chr(237), chr(237), chr(237), chr(237), chr(237), chr(237));
$this->numberingList[$id][3] = array(chr(248), chr(248), chr(248), chr(248), chr(248), chr(248), chr(248), chr(248), chr(248), chr(248), chr(248));
}
}
}
}

/**
* Extract the content of a w:p tag
*
* @access private
* @param $node object
* @return string
*/
private function printWP($node)
{
$ilvl = $numId = -1;
if ($this->list2text) {//transform the list in ooxml to formatted list with tabs and bullets
if (empty($this->numberingList)) {//check if numbering.xml is extracted from the zip archive
$this->listNumbering();
}
//use the xpath to get expecific children from a node
$xpath = new DOMXPath($this->domDocument);
$query = 'w:pPr/w:numPr';
$xmlLists = $xpath->query($query, $node);
$xmlLists = $xmlLists->item(0);

//if ($xmlLists->tagName == 'w:numPr') {
// if ($xmlLists->hasChildNodes()) {
// foreach ($xmlLists->childNodes as $child) {
// if ($child->tagName == 'w:ilvl') {
// $ilvl = $child->getAttribute('w:val');
// }elseif ($child->tagName == 'w:numId') {
// $numId = $child->getAttribute('w:val');
// }
// }
// }
//}
//if (($ilvl != -1) && ($numId != -1)) {
// //if is founded the style index of the list in the document and the kind of list
// $ret = '';
// for($i=-1; $i < $ilvl; $i++) {
// if(self::DEBUG) {
// $ret .= self::SEPARATOR_TAB_DEBUG;
// }
// else {
// $ret .= self::SEPARATOR_TAB;
// }
// }
// $ret .= array_shift($this->numberingList[$numId][$ilvl]) . ' ' . $this->toText($node); //print the bullet
//} else {
$ret = $this->toText($node);
//}
} else {
//if dont want to formatted lists, we strip from html tags
$ret = $this->toText($node);
}


//get the data from the charts
if ($this->chart2text) {
$query = 'w:r/w:drawing/wp:inline';
$xmlChart = $xpath->query($query, $node);
//get the relation id from the document, to get the name of the xml chart file from the relations to extract the xml code.
foreach ($xmlChart as $chart) {
foreach ($chart->childNodes as $child) {
foreach ($child->childNodes as $child2) {
foreach ($child2->childNodes as $child3) {
$rid = $child3->getAttribute('r:id');
}
}
}
}
//if (!empty($rid)) {
// if (empty($this->relations)) {
// $this->loadRelations();
// }
// //get the name of the chart xml file from the relations docuemnt
// $dataChart = new getDataFromXmlChart($this->docx->getFromName('word/' . $this->relations[$rid]['file']));
// if (in_array($this->chart2text, array(2, 'table'))) {
// $ret .= $this->printChartDataTable($dataChart);//formatted print of the chart data
// } else {
// $ret .= $this->printChartDataArray($dataChart);//formatted print of the chart data
// }
//}
}
//extract the expecific endnote to insert with the text content
if ($this->endnote2text) {
if (empty($this->endnotes)) {
$this->loadEndNote();
}
$query = 'w:r/w:endnoteReference';
$xmlEndNote = $xpath->query($query, $node);
foreach ($xmlEndNote as $note) {
$ret .= '[' . $this->endnotes[$note->getAttribute('w:id')] . '] ';
}
}
//extract the expecific footnote to insert with the text content
if ($this->footnote2text) {
if (empty($this->footnotes)) {
$this->loadFootNote();
}
$query = 'w:r/w:footnoteReference';
$xmlFootNote = $xpath->query($query, $node);
foreach ($xmlFootNote as $note) {
$ret .= '[' . $this->footnotes[$note->getAttribute('w:id')] . '] ';
}
}
if ((($ilvl != -1) && ($numId != -1)) || (1)) {
$ret .= $this->separator();
}

return $ret;
}

/**
* return a text end of line
*
* @access private
*/
private function separator()
{
return "\r\n";
}

/**
*
* Extract the content of a table node from the document.xml and return a text content
*
* @access private
* @param $node object
*
* @return string
*/
private function table($node)
{
$output = '';
if ($node->hasChildNodes()) {
foreach ($node->childNodes as $child) {
//start a new line of the table
if ($child->tagName == 'w:tr') {
foreach ($child->childNodes as $cell) {
//start a new cell
if ($cell->tagName == 'w:tc') {
if ($cell->hasChildNodes()) {
//
foreach ($cell->childNodes as $p) {
$output .= $this->printWP($p);
}
$output .= self::SEPARATOR_TAB;
}
}
}
}
$output .= $this->separator();
}
}
return $output;
}


/**
*
* Extract the content of a node from the document.xml and return only the text content and. stripping the html tags
*
* @access private
* @param $node object
*
* @return string
*/
private function toText($node)
{
$xml = $node->ownerDocument->saveXML($node);
return trim(strip_tags($xml));
}
}

// 实例化
$text = new Docx2Text();
// 加载docx文件
$text->setDocx('./1.docx');
// 将内容存入$docx变量中
$docx = $text->extract();
// 调试输出
var_dump($docx);

小结

代码中处理docx的类来自这里
其实docx就是xml的一种扩展类型的文档.

PHP检测url重定向的最终地址

引言

客户需求, 需要判断一个url跳转后的url是否是目标url, 于是有此文, 惯例先贴代码.

代码

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
/**
* 递归检测url重定向地址, 直到重定向到rule所指地址
* 返回该地址
*
* @param string $url 待检测的地址
* @param string $rule 匹配的地址
* @return mixed
*/
function redirect($url, $rule = 'https://www.google.com/')
{
$header = get_headers($url, 1);
//print_r($header);
if (strpos($header[0], '301') !== false || strpos($header[0], '302') !== false) {
// 检测到跳转
if (array_key_exists('Set-Cookie', $header)) {
// 检测到cookie, 进行设置
$cookies = $header['Set-Cookie'];
foreach ($cookies as $k => $v) {
header('Set-Cookie: ' . $v);
}
}
if (array_key_exists('Location', $header)) {
$url = $header['Location'];
if (is_array($url)) {
foreach ($url as $k => $v) {
if (strpos($v, $rule) !== false) {
// 跳转地址与$rule匹配, 返回该地址
return $v;
} else {
// 不匹配则访问一次中转网址
file_get_contents($v);
}
}
} else {
if (strpos($url, $rule) !== false) {
// 跳转地址与$rule匹配, 返回该地址
return $url;
}
}
}
}
return false;
}

小结

核心函数get_headers()
其余的就是常规的字符串判断函数.

Discuz X3.2插件开发(二)

引言

dz默认的邀请码功能不是很人性化, 默认没有提供添加邀请码的接口, 于是便使用插件的方式实现添加邀请码API, 惯例先贴核心代码

代码

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
<?php
if (!defined('IN_DISCUZ')) {
exit('Access Denied');
}

date_default_timezone_set('Asia/Shanghai');
global $_G;

/**
* 获取query之后的返回值, 并按需格式化
*
* @param $response
* @return array|bool
*/
function get_query_data($response)
{
while ($arr = mysqli_fetch_array($response)) {
$data[] = array('id' => $arr['id'], 'code' => $arr['code'], 'used' => $arr['status'] == 2 ? $arr['fusername'] . '使用于' . date('Y-m-d H:i:s', $arr['regdateline']) : '暂未使用');
}
return isset($data) ? $data : false;
}

if (isset($_POST['code'])) {
$link = mysqli_connect(
$_G['config']['db'][1]['dbhost'],
$_G['config']['db'][1]['dbuser'],
$_G['config']['db'][1]['dbpw'],
$_G['config']['db'][1]['dbname']
);
$table = $_G['config']['db'][1]['tablepre'] . 'common_invite';
if (empty($_POST['code'])) {
$res = mysqli_query($link, "SELECT * FROM `$table`");
$data = get_query_data($res);
} else {
switch ($_POST['submit']) {
case 'add':
$res = mysqli_query($link, "INSERT INTO `$table` (`code`) VALUES ('" . $_POST['code'] . "')");
$res = mysqli_query($link, "SELECT * FROM `$table`");
$data = get_query_data($res);
break;

case 'del':
mysqli_query($link, "DELETE FROM `$table` WHERE `code` = '" . $_POST['code'] . "'");
$res = mysqli_query($link, "SELECT * FROM `$table`");
$data = get_query_data($res);
break;
}
}
}
?>
<!DOCTYPE html>
<html lang="zh-cmn-Hans">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#db5945">
<link rel="stylesheet" href="//cdn.bootcss.com/bootstrap/3.3.5/css/bootstrap.min.css">
<title>邀请码Demo</title>
</head>
<body>
<div class="container">
<form target="_self" method="post" class="form-inline">
<input type="text" name="code" id="code" class="form-control" placeholder="邀请码">
<button type="submit" class="btn btn-success" name="submit" value="add">添加</button>
<button type="submit" class="btn btn-danger" name="submit" value="del">删除</button>
</form>
<div id="table" class="table-responsive">
<table class="table table-hover">
<thead>
<tr>
<td>编号</td>
<td>邀请码</td>
<td>使用情况</td>
</tr>
</thead>
<tbody>
<?php
if (isset($data)) {
foreach ($data as $each) {
echo '<tr><td>' . $each['id'] . '</td><td>' . $each['code'] . '</td><td>' . $each['used'] . '</td></tr>';
}
}
?>
</tbody>
</table>
</div>
</div>
</body>
</html>

小结

把代码放到新建一个文件夹里面, 把文件夹名字加到dz后台添加插件里, 选择导航插件, 例如快捷导航, 启用插件后即可在前台进入插件进行对邀请码的管理

Discuz X3.2插件开发(一)

准备

config/config_global.php 中添加一行

1
2
3
4
/*
1表示开发者模式, 2表示开发者模式并且显示页面hook
*/
$_config['plugindeveloper'] = 2;

插件xml语法

  1. 版本兼容
1
2
3
4
5
6
7
8
<item id="Data">
<item id="plugin">
......
</item>
......
<item id="version"><![CDATA[多个版本用逗号分隔, 如 X2,X2.5]]></item>
......
</item>

官方文档

Dz资料库