分类目录归档:php

php入门与提高::::::演道网php专栏提供一线php研发人员在学习工作中的经验,减少大家走的弯路,大量源码可以直接使用。

wordpress启用WP Super Subdomains后子域名登录失效

wordpress启用WP Super Subdomains后子域名登录失效

很开心,终于搞定了WP Super Subdomains。
虽然为了启用它,修改了固定链接,折腾了半小时,又把旧链接跳转到了新链接。
但是除了www.go2live.cn有登录状态,其它几个子域名都是未登录状态。
要编辑文章的时候也很无赖。查看源代码找到了http://www.go2live.cn/?p=id 的短链接,然后把这个id拷贝到编辑页面,就这样勉强过去了。。

这么玩了几天,突然想到,用户不是登录不了,不能发评论了,这可要不得。赶紧百度一下。终于解决了。涉及到两个文件。

1.在wp-config.php加入下面两行代码。

define('COOKIE_DOMAIN', 'go2live.cn');  #这里要顶级域名
define('COOKIEPATH', '/');

2.修改wp-includes/default-constants.php文件

$siteurl = get_site_option( 'siteurl' );#这个东东在后面配置的。按WP Super Subdomains的要求需要是www.go2live.cn
$siteurl = 'go2live.cn'; #重新设置成了go2live.cn

搞定,开心。又可以直接从文章页进去编辑了。

wordpress修改了固定链接后404怎么办?

wordpress修改了固定链接后404怎么办?

今天修改了下固定链接做成了多域名。
原来的固定链接http://go2live.cn/archives/191637.html

现在对应的页面是http://dev.go2live.cn/python/python%E5%AD%A6%E4%B9%A0%E6%89%8B%E5%86%8C.html。

还没有研究过wordpress的api。
查了下404的页面是在 wp-content/themes/你的主题/404.php

我的页面是

<?php get_header(); ?>

<div class="content-wrap">
        <div class="content">
                <?php hui_404() ?>
        </div>
</div>

<?php get_footer(); ?>

代码好简单。就是显示了下找不到内容,然后一个链接回首页。
观察到新的页面和id没有关系,是不可能通过apache或者nginx的rewrite直接重定向过去的。
只有写代码实现了。

今天编辑文章的时候注意到一个短链接形式 http://www.go2live.cn/?p=191637
然后这个链接会跳转到http://dev.go2live.cn/python/python%E5%AD%A6%E4%B9%A0%E6%89%8B%E5%86%8C.html。

好事,这样省略了我根据id去生成链接的过程,毕竟我还没有研究过wordpress api。
办法找到了。

1. http://go2live.cn/archives/191637.html
2. http://www.go2live.cn/?p=191637
3. http://dev.go2live.cn/python/python%E5%AD%A6%E4%B9%A0%E6%89%8B%E5%86%8C.html

很简单的解决方案,直接从链接1中正则解析出id,然后拼成链接2,跳转到链接2,就会重定向到链接3.
达到目的。

从Dash上搜了下redirect,发现真有一个函数wp_redirect做url跳转的事。
最后的代码:

<?php get_header(); ?>

<div class="content-wrap">
        <div class="content">
                <?php 
                if (preg_match('~/archives/(\d*)\.html.*~',$_SERVER['REQUEST_URI'],$matches))
                {   
                $postid = $matches[1];  
                wp_redirect( get_bloginfo('url').'?p='.$postid );
                exit;
                }   
                ?>  
                <?php hui_404() ?>
        </div>
</div>

<?php get_footer(); ?>

改固定链接容易,但是之前的链接可就全404了,这对seo来说可不是好事。。找办法重定向回正确的地址吧。
我干了两次这种事。
第一次通过rewrite规则解决。
第二次改写404.php解决。

文章简单,希望能帮助到你。

centos6.5安装coreseek替换wordpress的搜索功能

引言

实在受不了wordpress乌龟般慢的搜索速度了,感觉网站挂了似得。
之前已经苦逼过怎么优化wordpress了,参见另一篇文章数据过万后wordpress优化过程记录2, 也换成了全世界最好的编程语言php7,过程见于安装php7为wordpress提速。这次是要把wordpress自带的mysql like的搜索替换成全文搜索引擎。

安装coreseek

安装依赖包

yum install make gcc g++ gcc-c++ libtool autoconf automake imake mysql-devel libxml2-devel  expat-devel* mysql-server mysql

coreseek下载

官网打不开了,网上找了个包,放到了csdn上。

tar xzvf coreseek-3.2.14.tar.gz
cd coreseek-3.2.14

安装mmseg

$ cd mmseg-3.2.14
$ ./bootstrap    #输出的warning信息可以忽略,如果出现error则需要解决
$ ./configure --prefix=/usr/local/mmseg3
$ make && make install
$ cd ..
##如果提示libtool: unrecognized option `--tag=CC' ,请查看libtool问题解决方案
##安装完成后,mmseg使用的词典和配置文件,将自动安装到/usr/local/mmseg3/etc中
##中文分词测试,如果显示不正常,请检查当前环境下的locale和UTF-8中文字符显示设置
$  /usr/local/mmseg3/bin/mmseg -d /usr/local/mmseg3/etc mmseg-3.2.14/src/t1.txt
    中文/x 分/x 词/x 测试/x
    中国人/x 上海市/x

Word Splite took: 1 ms.

安装coreseek

$ cd csft-4.1
#执行configure,进行编译配置:
$ sh buildconf.sh
$ ./configure --prefix=/usr/local/coreseek  --without-unixodbc --with-mmseg --with-mmseg-includes=/usr/local/mmseg3/include/mmseg/ --with-mmseg-libs=/usr/local/mmseg3/lib/ --with-mysql
$ make && make install

测试coreseek

cd ../testpack
$  /usr/local/coreseek/bin/indexer -c etc/csft.conf

以下为正常情况下的提示信息:

Coreseek Fulltext 4.1 [ Sphinx 2.0.2-dev (r2922)]
Copyright (c) 2007-2011,
Beijing Choice Software Technologies Inc (http://www.coreseek.com)

ERROR: nothing to do.

$  /usr/local/coreseek/bin/indexer -c etc/csft.conf --all

以下为正常索引全部数据时的提示信息:
Coreseek Fulltext 4.1 [ Sphinx 2.0.2-dev (r2922)]
Copyright (c) 2007-2011,
Beijing Choice Software Technologies Inc (http://www.coreseek.com)

using config file ‘etc/csft.conf’…
indexing index ‘xml’…
collected 3 docs, 0.0 MB
sorted 0.0 Mhits, 100.0% done
total 3 docs, 7585 bytes
total 0.007 sec, 979088 bytes/sec, 387.24 docs/sec
total 3 reads, 0.000 sec, 2.8 kb/call avg, 0.0 msec/call avg
total 9 writes, 0.000 sec, 2.2 kb/call avg, 0.0 msec/call avg

$  /usr/local/coreseek/bin/indexer -c etc/csft.conf xml

以下为正常索引指定数据时的提示信息:
Coreseek Fulltext 4.1 [ Sphinx 2.0.2-dev (r2922)]
Copyright (c) 2007-2011,
Beijing Choice Software Technologies Inc (http://www.coreseek.com)

using config file ‘etc/csft.conf’…
indexing index ‘xml’…
collected 3 docs, 0.0 MB
sorted 0.0 Mhits, 100.0% done
total 3 docs, 7585 bytes
total 0.006 sec, 1260594 bytes/sec, 498.58 docs/sec
total 3 reads, 0.000 sec, 2.8 kb/call avg, 0.0 msec/call avg
total 9 writes, 0.000 sec, 2.2 kb/call avg, 0.0 msec/call avg

$  /usr/local/coreseek/bin/search -c etc/csft.conf

以下为正常测试搜索时的提示信息:
Coreseek Fulltext 4.1 [ Sphinx 2.0.2-dev (r2922)]
Copyright (c) 2007-2011,
Beijing Choice Software Technologies Inc (http://www.coreseek.com)

using config file ‘etc/csft.conf’…
index ‘xml’: query ”: returned 3 matches of 3 total in 0.000 sec

displaying matches:
1. document=1, weight=1, published=Thu Apr 1 22:20:07 2010, author_id=1
2. document=2, weight=1, published=Thu Apr 1 23:25:48 2010, author_id=1
3. document=3, weight=1, published=Thu Apr 1 12:01:00 2010, author_id=2

words:

$  /usr/local/coreseek/bin/search -c etc/csft.conf -a Twittter和Opera都提供了搜索服务

以下为正常测试搜索关键词时的提示信息:
Coreseek Fulltext 4.1 [ Sphinx 2.0.2-dev (r2922)]
Copyright (c) 2007-2011,
Beijing Choice Software Technologies Inc (http://www.coreseek.com)

using config file ‘etc/csft.conf’…
index ‘xml’: query ‘Twittter和Opera都提供了搜索服务 ‘: returned 0 matches of 0 total in 0.015 sec

words:
1. ‘twittter’: 1 documents, 3 hits
2. ‘和’: 3 documents, 15 hits
3. ‘opera’: 1 documents, 25 hits
4. ‘都’: 2 documents, 4 hits
5. ‘提供’: 0 documents, 0 hits
6. ‘了’: 3 documents, 18 hits
7. ‘搜索’: 2 documents, 5 hits
8. ‘服务’: 1 documents, 1 hits

$  /usr/local/coreseek/bin/searchd -c etc/csft.conf

以下为正常开启搜索服务时的提示信息:(csft-4.0版类似)
Coreseek Fulltext 4.1 [ Sphinx 2.0.2-dev (r2922)]
Copyright (c) 2007-2011,
Beijing Choice Software Technologies Inc (http://www.coreseek.com)

using config file ‘etc/csft.conf’…
WARNING: compat_sphinxql_magics=1 is deprecated; please update your application and config
listening on all interfaces, port=9312
precaching index ‘xml’
precached 1 indexes in 0.001 se

配置coreseek支持mysql数据源

配置csft_mysql.conf文件

复制mysql配置文件到coreseek安装目录etc/下(比如/usr/local/coreseek/etc/)

cp /usr/local/src/coreseek-3.2.14/testpack/etc/csft_mysql.conf /usr/local/coreseek/etc/
cd /usr/local/coreseek/etc/
vi csft_mysql.conf

源定义

source phperz
{
type = mysql

sql_host                = localhost
sql_user                = root
sql_pass                = xxxx
sql_db                    = phperz
sql_port                = 3306
sql_query_pre            = SET NAMES utf8

sql_query                = SELECT ID, post_author, UNIX_TIMESTAMP(post_date) AS post_date, post_title, post_content FROM wp_posts where post_status='publish' and post_type in ('page','post')
                                                          #sql_query第一列id需为整数
                                                          #title、content作为字符串/文本字段,被全文索引
sql_attr_uint            =  post_author#从SQL读取到的值必须为整数
sql_attr_timestamp        = post_date#从SQL读取到的值必须为整数,作为时间属性

sql_query_info_pre      = SET NAMES utf8                                        #命令行查询时,设置正确的字符集
sql_query_info            = SELECT * FROM wp_posts WHERE ID=$id #命令行查询时,从数据库读取原始数据信息

}

index定义

index phperz
{
source = phperz #对应的source名称
path = /usr/local/coreseek/var/data/phperz #请修改为实际使用的绝对路径,例如:/usr/local/coreseek/var/…
docinfo = extern
mlock = 0
morphology = none
min_word_len = 1
html_strip = 0

#中文分词配置,详情请查看:http://www.coreseek.cn/products-install/coreseek_mmseg/
charset_dictpath = /usr/local/mmseg3/etc/ #BSD、Linux环境下设置,/符号结尾
#charset_dictpath = etc/                             #Windows环境下设置,/符号结尾,最好给出绝对路径,例如:C:/usr/local/coreseek/etc/...
charset_type        = zh_cn.utf-8

}

全局index定义

indexer
{
mem_limit = 128M
}

searchd服务定义

searchd
{
listen = 9312
read_timeout = 5
max_children = 30
max_matches = 1000
seamless_rotate = 0
preopen_indexes = 0
unlink_old = 1
pid_file = /usr/local/coreseek/var/log/searchd_mysql.pid #请修改为实际使用的绝对路径,例如:/usr/local/coreseek/var/…
log = /usr/local/coreseek/var/log/searchd_mysql.log #请修改为实际使用的绝对路径,例如:/usr/local/coreseek/var/…
query_log = /usr/local/coreseek/var/log/query_mysql.log #请修改为实际使用的绝对路径,例如:/usr/local/coreseek/var/…
}

建立索引

路经部分需要改成你自己的地址
/usr/local/coreseek/bin/indexer -c /usr/local/coreseek/etc/csft_mysql.conf –all

可能出现的错误
ERROR: index ‘phperz’: sql_connect: Can’t connect to local MySQL server through socket ‘/var/lib/mysql/mysql.sock’ (2) (DSN=mysql://root:***@localhost:3306/phperz).
这是因为mysql的sock文件路经不正确导致的.
确认一下你的mysql.sock路经,建立一个软连接,比如
ln -s /tmp/mysql.sock /var/lib/mysql/mysql.sock

coreseek+php使用方式

复制安装目录下的/usr/local/src/coreseek-4.1-beta/testpack/api/sphinxapi.php文件到你的项目里
你的程序里include sphinxapi.php
php使用方法见/usr/local/src/coreseek-4.1-beta/testpack/api/test.php

wordpress修改, 把api目录拷贝到主题目录中后,修改search.php,我的修改如下(和主题相关,不能完全照抄,仅供参考)

<?php get_header(); ?>
<?php
//coreseek搜索结果
include_once( dirname(__FILE__) . "/api/sphinxapi.php" );
$cl = new SphinxClient ();
$cl->SetServer ( '127.0.0.1', 9312);
$cl->SetConnectTimeout(3);
$cl->SetMatchMode(1);
//以下设置用于返回数组形式的结果
$cl->SetArrayResult ( true );

$paged = (get_query_var('paged')) ? get_query_var('paged') : 1;
$start = ($paged - 1) * 10;
$cl->SetLimits($start,10);

$keyword =  $s = isset($_GET['s']) ? htmlspecialchars(trim($_GET['s'])) : ''; //获取搜索词
$res = $cl->Query ( "$keyword", "*" );


#print_r($res); //查看全文索引结果
$total = $res['total'];  //所有返回文章数,用于分页

if ( $res===false )
{
    print "Query failed: " . $cl->GetLastError() . ".\n";

} else
{
    if ( $cl->GetLastWarning() )
        print "WARNING: " . $cl->GetLastWarning() . "\n\n";
}
$have_posts = true;
if(isset($res['matches']) && !empty($res['matches'])) {
    foreach($res['matches'] as $value) {
        $id_arr[] = $value['id'];
    }
}
else
{
$have_posts = false;
}

$id_str = @implode(",", $id_arr);
$args  = array();
$args = array( 'include' => $id_arr);
wp_reset_query();
$sql = "select * from wp_posts where ID in($id_str) and post_type in ('post','page') AND post_status = 'publish'";  //根据ID读取文章数据
$data = $wpdb->get_results($sql);  //输出数据,之后你可以使用foreach数据$post object
?>
<div class="content-wrap">
        <div class="content">
                <?php echo _hui('ads_search_01_s') ? '<div class="ads ads-content">'.hui_get_adcode('ads_search_01').'</div>' : '' ?>
                <h1 class="title"><strong><?php echo htmlspecialchars($s); ?> <?php echo __('的搜索结果', 'haoui') ?></strong></h1>
                <?php if ( !$have_posts ) : ?>
                        <h3 class="text-muted text-center"><?php echo __('暂无搜索结果', 'haoui') ?></h3>
                <?php else: ?>
                        <?php get_template_part( 'excerpt' );  ?>
                <?php endif; ?>
        </div>
</div>

<?php
get_sidebar();

get_footer();
?>



coreseek日常维护

启动
/usr/local/coreseek/bin/searchd -c /usr/local/coreseek/etc/csft_mysql.conf
停止
/usr/local/coreseek/bin/searchd -c /usr/local/coreseek/etc/csft_mysql.conf –stop
建立索引
/usr/local/coreseek/bin/indexer -c /usr/local/coreseek/etc/csft_mysql.conf –all
重建索引
/usr/local/coreseek/bin/indexer -c /usr/local/coreseek/etc/csft_mysql.conf –all –rotate

你需要把启动命令加到开机自启动里
把重建索引命令加到计划任务里每天执行

后记

折腾了3小时才搞定,希望对大家有用。可以访问我的网站试试搜索效果。写这篇博客的时候,我的博客已经快3万了哦。

经查证,直接修改search.php没有用。因为sql查询在之前就已经查了,search.php相当于一个模板页面了。

阅读完原代码,修改了wp-include/query.php, 终于优化完成了。

优化前:

Page generated in 2.18491s, 38.61% PHP, 61.39% MySQL

优化后:

Page generated in 0.54773s, 95.51% PHP, 4.49% MySQL

 

安装php7为wordpress提速

前言

PHP是应用非常广泛的动态语言,其简单直接的语法,优异的性能及与LNMP/LAMP的黄金组合,得到了很多开发者的认可。PHP 7比PHP 6整体性能提高了一倍,自其一发布就引来了全球PHP开发者的强烈关注。本文就PHP7的安装作具体说明。

内容

安装前的准备

系统:CentOS 6.5

Web服务器:Nginx v1.9.12

下载PHP 7:http://php.net/downloads.php
打开比较慢,php7.0.10 可以直接下载

正式安装

  1. 安装PHP相关依赖
# yum install libxml2 libxml2-devel openssl-devel bzip2-devel libcurl-devel enchant enchant-devel libpng-devel gmp-devel libc-client libc-client-devel pam-devel firebird-devel libicu-devel openldap openldap-devel libmcrypt-devel unixODBC-devel freetds freetds-devel
  1. 编译安装PHP 7

PHP 5.6及以上已经内置了php-fpm,不需要再使用外挂的方式安装php-fpm。只需要编译时加上“–enable-fpm”参数即可。

# cd /path/to/downloads
# tar php-7.0.10.tar.gz
# cd php-7.0.10
# ./configure --prefix=/usr/local/php-7.0.10 --enable-fpm --with-xml --with-curl --enable-mbstring  --with-mcrypt --with-mysqli=mysqlnd --with-pdo-mysql=mysqlnd --with-zlib  --with-gd --with-freetype-dir --enable-gd-native-ttf --enable-gd-jis-conv --with-gettext --with-openssl 
# make && make install && make clean

以上为只标注了部份php扩展,如需打开全部扩展,可以使用”–enable-all”,如若需要查看具体参数说明,可通过如下命令查看:

# ./configure --help

配置PHP

  1. 配置php.ini文件

display_errors=Off
default_time_zone=Asia/Chongqing

  1. 启动php-fpm
  2. # /usr/local/php-7.0.4/sbin/php-fpm -D
    

    3.配置测试虚拟机

    # mkdir /webapps /webapps/test.mydomain.com
    # chmod -R 755 /webapps/test.mydomain.com
    

    增加nginx站点设置:

server {
       listen       80;
       server_name test.mydomain.com;
       root /webapps/test.mydomain.com;
       index index.html index.php;
       # 如果需要php, php-fpm运行环境
       location ~ \.php$ {
           fastcgi_pass 127.0.0.1:9000;
           fastcgi_index index.php;
           fastcgi_param SCRIPT_FILENAME  $document_root$fastcgi_script_name;
           include fastcgi_params;
       }
}

增加test.mydomain.com本机hosts,如下:

# vim /etc/hosts
127.0.0.1 test.mydomain.com

编写测试文件

# vim phpinfo.php
<?php phpinfo();
重启nginx与php-fpm服务:

# service nginx reload
# killall -TERM php-fpm
# /usr/local/php-7.0.4/sbin/php-fpm -D

打开浏览器,输入地址http://php.mydomain.com/phpinfo.php,即可看到php相关环境信息输出页面。

写在最后

编译安装php7感觉比较麻烦,一开始是mysqli没有安装好,结果wordpress运行不起来。后来上传插件安装,又发现缺少zlib库。现在又发现缺少gd库。
不过不管怎么说,wordpress性能又提升了2倍,还是不错的。其它优化方式可以参考另一篇文章

数据过万后wordpress优化过程记录

数据过万后wordpress优化过程记录

导读

当文章数到3万之后,网站巨慢无比,无法忍受(各种缓存插件都试过了)。为了改善这个情况,

服务器升级

  1. 立马买了rds。 然后感觉好了点。。
  2. 升级服务器配置改成2核4G内存
    但是直觉是错的。运行top命令看了下,发现CPU占比太高了,然后升级服务器配置,升级成2核4G内存。
    感觉还是没用啊。。用ab测试工具 ab -n 1000 -c 100 http://test.com/indexphp
    测试了下,QPS居然只有1.75。

替换apache为nginx

  1. 更换apache为nginx,nginx的并发能力比apache高不少。
    安装nginx和配置方法
    再测试
内容 apache nginx
CPU 100% 100%
负载 100 50
内存 耗光 几乎耗光
QPS 1.78 4.2

降请求,降IO

把图片等通过CDN加速,把请求转移走,可以使用阿里云的CDN也可以使用七牛的。
七牛免费,而且有现成的插件。

大概说下CDN的原理。

1. 你原网站的图片不动,该怎么还是怎么样。譬如我的站点是http://go2live.cn,访问图片是http://www.go2live.cn/wp-content/uploads/2016/09/warticle_26361472786988577-150x1501.jpg
2. 找个CDN,如七牛的。需要实名认证,并有10元余额才可以开通自定义域名的CDN。
我创建的加速域名是  http://img.go2live.cn
3. wordpress中更改图片链接的域名为img.go2live.cn
4. 当用户从img.go2live.cn去访问图片时,图片是不存在的,这时CDN会从源站,既http://go2live.cn去拉取资源再显示,下次其他用户再访问同样的资源就有图片了。

配置时间比较长,我先拿阿里云的CDN做个测试。

大概说下CNAME配置

1. 不管是七牛还是阿里云CDN,都会给你一个CNAME记录。
2. 同时记得你设置过一个加速域名。我的是img.go2live.cn
3. 在域名配置里加一个CNAME记录。主机记录,我的为img, 记录值,即为CNAME值。

再测试一下:效果不大

内容 apache nginx
CPU 100% 94%
负载 100 45
内存 耗光 余74M
QPS 1.78 4.08

参考
ab -n 1000 -c 100 http://www.go2live.cn/wp-content/uploads/2016/09/warticle_26361472786988577-150×1501.jpg
得出的静态图片QPS是11698

程序问题

从rds中找到如下slow sql

1. SELECT wp_posts.* FROM wp_posts WHERE 1=1 AND wp_posts.post_type = 'post' AND ((wp_posts.post_status = 'publish')) ORDER BY wp_posts.post_date DESC
耗时32秒
2. SELECT SQL_CALC_FOUND_ROWS wp_posts.ID FROM wp_posts WHERE 1=1 AND (((wp_posts.post_title LIKE '%互联网产品技术热点一角-关注成长%') OR (wp_posts.post_excerpt LIKE '%互联网产品技术热点一角-关注成长%') OR (wp_posts.post_content LIKE '%互联网产品技术热点一角-关注成长%'))) AND wp_posts.post_type IN ('post', 'page', 'attachment') AND (wp_posts.post_status = 'publish' OR wp_posts.post_author = 1 AND wp_posts.post_status = 'private') ORDER BY wp_posts.post_title LIKE '%互联网产品技术热点一角-关注成长%' DESC, wp_posts.post_date DESC LIMIT 0, 10
耗时17秒

这不只是sql 查询慢的问题,他把整个数据都查到内存里,也造成了内存基本耗光。

优化方法只能针对这些使用场景:

  1. 加缓存。
  2. 分批次读取,而不是一次性读取,减少内存的使用。
  3. 预加载,把目标数据放到cron里先跑出来。

有个插件【Import External Images】,我就是用方法2优化的,在csdn上有下载

推荐一个插件【Query Monitor】,可以用来查找程序的性能瓶颈。

加大并发数一定是加各种缓存

缓存插件

仔细检查了下,发现我的【WP Super Cache】并没有生效。弄好之后,重新测试QPS到了155

这个是磁盘缓存,换成memcache缓存肯定更好。参考这个.
不知道为啥,没有生效。

用上阿里的OCS看看,参考文章

看起来是生效了。再做下ab测试。

内容 apache nginx
CPU 100% 94%
负载 100 43
内存 耗光 余1G
QPS 1.78 4.03

原来memcache是开在本机的,现在转到OCS,内存是降了,但也看出了真正的瓶颈是CPU。这不得不说是代码问题,有太多的计算量了。
真的除了加各种缓存把动态的php变成静态的页面,没有更好的方法了。
从这里其实也看出来了, 【WP Super Cache】的对象缓存功能并没有生效。通过Debug和看代码,发现原因。

日志

Non empty GET request. Not serving request from object cache. {"url":"/archives/92943.html"}

代码wp-cache-phase2.php

elseif ( $wp_cache_object_cache && !empty( $_GET ) ) {
        wp_cache_debug( 'Not caching GET request while object cache storage enabled.', 5 );
        $cache_this_page = false;
    }

代码wp-cache-phase1.php

if ( !empty( $_GET ) ) { 
            wp_cache_debug( "Non empty GET request. Not serving request from object cache. " . json_encode( $_GET ), 1 );
            return false;
        }  

从日志里可以看到,总会有一个url的值。所以都会判断为Get请求。
把!empty((_GET) 改成 count()_GET)>1。

后记:后来发现是nginx的rewrite规则问题。其它人在这个地方应该不会有事。

第2个问题是, $meta 存的时候是

$serial = '<?php die(); ?>' . json_encode( $wp_cache_meta );

取的时候是

$meta = json_decode( wp_cache_get( $meta_filename, 'supercache' ), true );

结果导致meta无效,不启用缓存。

终于搞定。bug误人啊。。现在的QPS是128

nginx前端缓存

参考这个
这个肯定是最有效的,因为它会动态的页面变成了静态页面,前文静态图片的并发量有11698,他同时带来的问题是缓存失效之后怎么更新。(文章更新、有新的评论数据,原来的缓存就是有问题的,需要干掉,重新建立缓存)

总结

  1. wordpress运算量太大,特耗CPU,在升级服务器硬件时,加强CPU。
  2. 改由单独的mysql感觉作用不大,不过用阿里云的服务的好处是,他有备份,有性能优化,独立的服务器不会受网站影响。
  3. CDN的好处是降低了请求量,对并发的提升有一定的提升,更主要的是把网站服务器的带宽分离出去了。
  4. OCS同样是独立的memcache, 服务能更健壮,而且有命中率什么的数据图片,心里有底。同时可以降低数据的访问量,Wordpress本是为个人定的博客程序,数据库的设计并非为大量数据而设计。可以在slow query中看到好多慢查询。
  5. nginx确实比apache的并发数更高,而且可以弄反向代理,搞伪静态化。
  6. 插件【WP Super Cache】,是降低了PHP的运算量,但是对于我几万文章的博客站来说,感觉作用不大。开始了对象缓存,看到ocs的数据是增加了,但是测试的时候QPS没啥变化。还是静态页面有效,能测试到。

参考

这个讲的全

centos6.5安装nginx+php+mysql


1.nginx 安装

        查看yum下nginx版本信息

                 

yum list | grep nginx

                  发现版本太低,手动添加nginx的yum仓库:

   

 vi /etc/yum.repos.d/nginx.repo

 

        内容:

      

[nginx]  
name=nginx repo  
baseurl=http://nginx.org/packages/centos/$releasever/$basearch/  
gpgcheck=0  
enabled=1

编辑保存之后再查看nginx版本

      安装:

yum install -y nginx

      安装完成之后可以通过 service nginx start 来启动服务

        注:

/etc/init.d/nginx start # 启动Nginx服务
/etc/init.d/nginx stop # 停止Nginx服务
/etc/nginx/nginx.conf # Nginx配置文件位置

nginx的安装到此完成,启动之后可以在浏览器ip访问,会显示nginx的页面

 

2.安装php、php-fpm

 

2.1 安装

先下载php5.6版本,下载的源码放在/usr/local/src/ 下

cd /usr/local/src/
wget http://php.net/distributions/php-5.6.8.tar.gz
tar -xcvf php-5.6.8.tar.gz

编译php需要一些依赖包:

yum install -y libxml2 libxml2-devel openssl-devel libjpeg libjpeg-devel libpng libpng-devel freetype freetype-devel libmcrypt libmcrypt-devel mcrypt mhash

configure:

./configure --prefix=/usr/local/php5 --with-config-file-path=/usr/local/php5/etc --enable-fpm --disable-ipv6 --enable-pdo --with-pdo-mysql --with-openssl --with-mcrypt --with-mhash --enable-json --enable-mbstring --with-gd --with-openssl-dir --with-jpeg-dir --with-png-dir --with-zlib-dir --with-freetype-dir --enable-gd-native-ttf --enable-gd-jis-conv --enable-zip

安装:

make
make install

安装完成之后设置fpm

cp /usr/local/php5/etc/php-fpm.conf.default /usr/local/php5/etc/php-fpm.conf  
vi php-fpm.conf 
// 找到如下几行,确保如下几行前没有”;” 
pid = run/php-fpm.pid 
error_log = log/php-fpm.log 
log_level = notice 
listen = 127.0.0.1:9000 
pm = dynamic 
pm.max_children = 50 
pm.start_servers = 20 
pm.min_spare_servers = 5 
pm.max_spare_servers = 35 
pm.max_requests = 500

pm表示使用哪种方式,有两个值可以选择,就是static(静态)或者dynamic(动态)。在更老一些的版本中,dynamic被称作apache-like。这个要注意看配置文件的说明。

下面4个参数的意思分别为:
pm.max_children:静态方式下开启的php-fpm进程数量。
pm.start_servers:动态方式下的起始php-fpm进程数量。
pm.min_spare_servers:动态方式下的最小php-fpm进程数量。
pm.max_spare_servers:动态方式下的最大php-fpm进程数量。

如果dm设置为static,那么其实只有pm.max_children这个参数生效。系统会开启设置数量的php-fpm进程。
如果dm设置为 dynamic,那么pm.max_children参数失效,后面3个参数生效。
系统会在php-fpm运行开始 的时候启动pm.start_servers个php-fpm进程,
然后根据系统的需求动态在pm.min_spare_servers和 pm.max_spare_servers之间调整php-fpm进程数。

利用php自带的php-fpm管理工具,可以很方便的start,stop,restart
把管理工具从源码包里放到php5/sbin文件夹里,方便使用

cp /usr/local/src/php-5.6.8/sapi/fpm/init.d.php-fpm /usr/local/php5/sbin/
cd /usr/local/php5/sbin/
chmod 755 init.d.php-fpm
./init.d.php-fpm start
Starting php-fpm  done

 

2.2 配置php session

cd /usr/local/php5/etc
vi php.ini
#搜索 session.save_path , 去掉前面的 ;
session.save_path = "/var/lib/php/session"  ; #此处假定session 保存在 /var/lib/php/session 目录下
#建立目录
mkdir -p /var/lib/php/session
chmod -R 777 /var/lib/php/session/  #更改目录权限

 

2.3 yum php 5.6 源配置

(可前往 https://webtatic.com/packages/php56/ 查看)

 

2.3.1 yum更新

163、aliyun镜像的php版本太低,需要对yum源进行更新

CentOS/RHEL 7.x:

rpm -Uvh https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
rpm -Uvh https://mirror.webtatic.com/yum/el7/webtatic-release.rpm

CentOS/RHEL 6.x:

rpm -Uvh https://dl.fedoraproject.org/pub/epel/epel-release-latest-6.noarch.rpm
rpm -Uvh https://mirror.webtatic.com/yum/el6/latest.rpm

 

2.3.2 查看安装php56命令

yum list php56*  #可以查看php5.6的各种包

执行 yum -y install *** 即可

 

 

3.安装MYSQL-Server

 

3.1 Yum安装

系统自带的yum库并没有需要版本的mysql,因此先得去mysql官网下载相应的repo对yum库进行扩展

cd /usr/local/
mkdir yumrepo
cd yumrepo
wget http://repo.mysql.com/mysql-community-release-el6-5.noarch.rpm
yum localinstall mysql-community-release-el6-5.noarch.rpm #安装yum库
#查看仓库是否安装成功
yum repolist enabled | grep "mysql.*-community.*
yum install -y mysql-community-server mysql-community-client mysql-community-dever

安装成功之后,输入命令启动mysql

service mysqld start

第一次启动会出现一堆提示:

这里的提示简单的说就是要我们用mysqladmin 对root账户的密码进行设置等等。。。

/usr/bin/mysqladmin -u root password '111111' #设置root密码为六个1

这个告诉我们/etc/my.cnf是首次运行生成的mysql配置文件(优化数据库就需要进这个配置文件配置相关的参数,此处不展开)。

cat /etc/my.cnf

yum 方式安装到此为止

 

3.2 Rpm安装

(只针对centos 6.5 64)

略去查看操作系统版本…

cd /usr/local/src/
mkdir mysql-server-5.6.21

下载三个rpm软件包:
MySQL-client-5.6.21-1.rhel5.x86_64.rpm
MySQL-devel-5.6.21-1.rhel5.x86_64.rpm
MySQL-server-5.6.21-1.rhel5.x86_64.rpm

cd mysql-server-5.6.21
wget http://dev.mysql.com/Downloads/MySQL-5.6/MySQL-server-5.6.21-1.rhel5.x86_64.rpm
wget http://dev.mysql.com/Downloads/MySQL-5.6/MySQL-devel-5.6.21-1.rhel5.x86_64.rpm
wget http://dev.mysql.com/Downloads/MySQL-5.6/MySQL-client-5.6.21-1.rhel5.x86_64.rpm

安装依赖:

yum install -y libaio

安装mysql

rpm -ivh MySQL-server-5.6.21-1.rhel5.x86_64.rpm
rpm -ivh MySQL-client-5.6.21-1.rhel5.x86_64.rpm 
rpm -ivh MySQL-devel-5.6.21-1.rhel5.x86_64.rpm

修改配置文件

cp /usr/share/mysql/my-default.cnf /etc/my.cnf

初始化mysql

/usr/bin/mysql_install_db

启动mysql

service mysql start

查看进程

ps -ef | grep mysql

    netstat -anpt | grep 3306

安装过程中会随机生成root密码并保存在/root/.mysql_secret  文件中,用more命令查看

more /root/.mysql_secret

然后用查询的密码登录mysql

mysql -uroot -p********   #****为你查询到的密码

修改密码

SET PASSWORD = PASSWORD('123456');   #123456 为你要设置的root密码

配置mysqld 启动mysql

cp /usr/share/mysql/mysql.server /etc/init.d/mysqld
service mysqld start
chkconfig mysqld on    #设置开机启动

 

 

 

到此mysql安装完成

 

 

4.配置nginx支持php

 

cd /etc/nginx/
vi nginx.conf
#打开gzip
gzip    on;

 

配置vhost,假设域名为www.a.com

cd /etc/nginx/conf.d/
vi www.a.com.conf
#内容如下
server {
        listen 80;
        server_name www.a.com a.com;
        #让不带www的域名跳转到带www的域名
        if($host != 'www.a.com') {
                rewrite ^(.*)$ http://www.a.com/$1 permanent;
        }
        location / {
                #开启ssi支持shtml
                ssi on;
                ssi_silent_errors on;
                ssi_types text/shtml;
                index index.shtml index.php index.htm index.html;
                root /mnt/www/www.a.com;
                #框架路由设置
                if ( !-e $request_filename ) {
                        rewrite ^(.*)$ /index.php?url=$1 last;
                }
        }

        location ~.php$ {
                root /mnt/www/www.a.com;
                fastcgi_pass 127.0.0.1:9000;
                fastcgi_index index.php;
                fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
                include fastcgi_params;
        }

        location ~.(jpg|jpeg|png|js|css) {
                root /mnt/www/www.a.com;
                expires 30d;
        }

}

测试一下 配置文件是否有错误

/etc/init.d/nginx configtest
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

在数据盘mnt上,新建文件夹/mnt/www/www.a.com,并新建info.php测试文件,内容为

<?php
phpinfo();
?>

在浏览器访问www.a.com/info.php,能显示信息则配置完成

ps:

#配置该网站用户管理
groupadd group_web #group_web 可以根据个人需求命名
useradd user_web -d /mnt/www/www.a.com
chown user_web:group_web /mnt/www/www.a.com

 

5.phpMyAdmin 安装配置

 

5.0 检查并修改php对mysql和mysqli支持

phpinfo() 输出可以看到当前php对mysql的支持程度,假设在页面输出信息里面并没有找到mysql的支持

cd /usr/local/src/php-5.6.8/ext/mysql
/usr/local/php5/bin/phpize    #执行这句命令生成configure文件
./configure --with-php-config=/usr/local/php5/bin/php-config --with-mysql=shared --enable-shared

编译安装 make && make install

mysqli的编译过程也是类似

cd /usr/local/src/php-5.6.8/ext/mysqli
/usr/local/php5/bin/phpize    #执行这句命令在mysqli目录下生成configure

此时可以查看目录下是否生成了configure可执行文件

./configure --with-php-config=/usr/local/php5/bin/php-config --enable-embedded-mysqli=shared --enable-shared

没有错误的话继续

make
make install

一切正常的话,此时会提示你 mysqli.so 生成,当然也可以全盘搜下

find / -name "mysqli.so"
#拷贝 mysql.so mysqli.so 到 php5/ext/
cd /usr/local/php5/
mkdir ext
cp /usr/local/php5/lib/php/extensions/no-debug-non-zts-20131226/mysqli.so /usr/local/php5/ext/ #mysqli.so 路径为上面命令查找所得
cp /usr/local/php5/lib/php/extensions/no-debug-non-zts-20131226/mysql.so /usr/local/php5/ext/

切换到php5目录查看php.ini 是否存在,不存在则拷贝源码目录下的

cd /usr/local/php5/etc
vi php.ini
#添加下面内容
extension_dir = "/usr/local/php5/ext";
extension = mysqli.so ;
extension = mysql.so ;

 

5.1 安装部署phpMyAdmin

下载phpMyAdmin (http://www.phpmyadmin.net/home_page/index.php),解压后上传到www目录下(tar上传解压也行),把文件夹重命名为 phpMyAdmin,(方便访问)

 cd /mnt/www/phpMyAdmin
cp config.sample.inc.php config.inc.php 
vi config.inc.php
#修改以下内容
$cfg['blowfish_secret'] = 'www.oschina.com';  #这里的内容随意设置,在cookie访问模式下不能为空
/* Authentication type */
$cfg['Servers'][$i]['auth_type'] = 'cookie';
/* Server parameters */
$cfg['Servers'][$i]['host'] = '127.0.0.1';  #重要,这里设置成localhost有可能会出现phpMyAdmin无法访问数据库
$cfg['Servers'][$i]['connect_type'] = 'tcp';
$cfg['Servers'][$i]['compress'] = false;
$cfg['Servers'][$i]['AllowNoPassword'] = false;
$cfg['Servers'][$i]['user'] = 'root';
$cfg['Servers'][$i]['password'] = '111111';  #这里设置数据库的密码

 上面的 host = 127.0.0.1 再强调一遍,设置成localhost有可能会无法访问数据库

        接下来就是继续配置nginx

cd /etc/nginx/conf.d/
vi phpmyadmin.conf
#文件内容
server {
    listen       9090;
    server_name  localhost;
    #charset koi8-r;
    #access_log  /var/log/nginx/log/host.access.log  main;
    location / {
        ssi on;
        ssi_silent_errors on;
        ssi_types text/shtml;
        root   /mnt/www/phpMyAdmin;
        index  index.html index.htm index.php;
        if ( !-e $request_filename ) {
                        rewrite ^(.*)$ /index.php?url=$1 last;
        }
    }
    location ~.php$ {
        root           /mnt/www/phpMyAdmin;
        fastcgi_pass   127.0.0.1:9000;
        fastcgi_index  index.php;
        fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
        include        fastcgi_params;
    }
    # deny access to .htaccess files, if Apache's document root
    # concurs with nginx's one
    #
    #location ~ /.ht {
    #    deny  all;
    #}
    location ~.(jpg|jpeg|png|js|css) {
                root /mnt/www/phpMyAdmin;
                expires 30d;
    }
}

保存退出后,重启nginx服务,重启mysqld服务,即可。

PHP面试常用算法

  一、冒泡排序

  基本思想:

    对需要排序的数组从后往前(逆序)进行多遍的扫描,当发现相邻的两个数值的次序与排序要求的规则不一致时,就将这两个数值进行交换。这样比较小(大)的数值就将逐渐从后面向前面移动。

  //冒泡排序

 
 1 php
 2 
 3     function mysort($arr)
 4     {
 5         for($i = 0; $i < count($arr); $i++)
 6         {
 7             $isSort = false;
 8             for ($j=0; $j< count($arr) - $i - 1; $j++) 
 9             {
10                 if($arr[$j] < $arr[$j+1])
11                 {
12                     $isSort = true;
13                     $temp = $arr[$j];
14                     $arr[$j] = $arr[$j+1];
15                     $arr[$j+1] = $temp ;
16                 }
17             }
18             if($isSort)
19             {
20                 break;
21             }
22         }
23         return $arr;
24     }
25 
26     $arr = array(3,1,2);
27     var_dump(mysort($arr));
28 ?>

View Code

   二、快速排序

  基本思想:

    在数组中挑出一个元素(多为第一个)作为标尺,扫描一遍数组将比标尺小的元素排在标尺之前,将所有比标尺大的元素排在标尺之后,通过递归将各子序列分别划分为更小的序列直到所有的序列顺序一致。

  //快速排序

 
 1 php
 2     //快速排序
 3         function quick_sort($arr) 
 4         {
 5             //先判断是否需要继续进行
 6             $length = count($arr);
 7             if($length <= 1) 
 8             {
 9                 return $arr;
10             }
11         
12             $base_num = $arr[0];//选择一个标尺  选择第一个元素
13 
14             //初始化两个数组
15             $left_array = array();//小于标尺的
16             $right_array = array();//大于标尺的
17             for($i=1; $i<$length; $i++) 
18             {            //遍历 除了标尺外的所有元素,按照大小关系放入两个数组内
19                 if($base_num > $arr[$i]) 
20                 {
21                     //放入左边数组
22                     $left_array[] = $arr[$i];
23                 } 
24                 else 
25                 {
26                     //放入右边
27                     $right_array[] = $arr[$i];
28                 }
29             }
30             //再分别对 左边 和 右边的数组进行相同的排序处理方式
31             //递归调用这个函数,并记录结果
32             $left_array = quick_sort($left_array);
33             $right_array = quick_sort($right_array);
34             //合并左边 标尺 右边
35             return array_merge($left_array, array($base_num), $right_array);
36         }
37 
38         $arr = array(3,1,2);
39         var_dump(quick_sort($arr));
40 
41 ?>

View Code

  

  三、二分查找

  基本思想:

    假设数据是按升序排序的,对于给定值x,从序列的中间位置开始比较,如果当前位置值等于x,则查找成功;若x小于当前位置值,则在数列的前半段中查找;若x大于当前位置值则在数列的后半段中继续查找,直到找到为止。(数据量大的时候使用)

  //二分查找

 1 php
 2     //二分查找
 3     function bin_search($arr,$low,$high,$k)
 4     {
 5         if($low <= $high)
 6         {
 7             $mid = intval(($low + $high)/2);
 8             if($arr[$mid] == $k)
 9             {
10                 return $mid;
11             }
12             else if($k < $arr[$mid])
13             {
14                 return bin_search($arr,$low,$mid-1,$k);
15             }
16             else
17             {
18                 return bin_search($arr,$mid+1,$high,$k);
19             }
20         }
21         return -1;
22     }
23 
24     $arr = array(1,2,3,4,5,6,7,8,9,10);
25 
26     print(bin_search($arr,0,9,3));
27 ?>

View Code

 

  四、顺序查找

  基本思想:

    从数组的第一个元素开始一个一个向下查找,如果有和目标一致的元素,查找成功;如果到最后一个元素仍没有目标元素,则查找失败。

    //顺序查找 

复制代码
 1 php
 2     //顺序查找
 3     function seq_search($arr,$n,$k)
 4     {
 5         $array[$n] = $k;
 6         for($i = 0;$i < $n; $i++)
 7         {
 8             if($arr[$i] == $k)
 9             {
10                 break;
11             }
12         }
13 
14         if($i < $n)
15         {
16             return $i;
17         }
18         else
19         {
20             return -1;
21         }
22     }
23 ?>

View Code

  

  五、写一个函数,能够遍历一个文件下的所有文件和子文件夹

 
 1 php    
 2     function my_scandir($dir)
 3     {
 4         $files = array();
 5         if($handle = opendir($dir))
 6         {
 7             while (($file = readdir($handle))!== false) 
 8             {
 9                 if($file != '..' && $file != '.')
10                 {
11                     if(is_dir($dir."/".$file))
12                     {
13                         $files[$file]=my_scandir($dir."/".$file);
14                     }
15                     else
16                     {
17                         $files[] = $file;
18                     }
19                 }
20             }
21 
22             closedir($handle);
23             return $files;
24         }
25     }
26 
27     var_dump(my_scandir('../'));
28 ?>

View Code

    六、写一个函数,尽可能高效的从一个标准url中取出文件的扩展名

 
 1 php
 2     function getExt($url)
 3     {
 4         $arr = parse_url($url);//parse_url解析一个 URL 并返回一个关联数组,包含在 URL 中出现的各种组成部分
 5         //'scheme' => string 'http' (length=4)
 6         //'host' => string 'www.sina.com.cn' (length=15)
 7         //'path' => string '/abc/de/fg.php' (length=14)
 8         //'query' => string 'id=1' (length=4)
 9         $file = basename($arr['path']);// basename函数返回路径中的文件名部分
10         $ext = explode('.', $file);
11         return $ext[count($ext)-1];
12     }
13 
14     print(getExt('http://www.sina.com.cn/abc/de/fg.html.php?id=1'));
15 
16 ?>

View Code

  七、实现中文字符串截取无乱码的方法

    可使用mb_substr,但是需要确保在php.ini中加载了php_mbstring.dll,即确保“extension=php_mbstring.dll”这一行存在并且没有被注释掉,否则会出现未定义函 数的问题。

八大排序算法(冒泡,简单选择,直接插入,快速排序,希尔排序,归并排序,堆排序,基数排序)之代码实现(js&php版本) 

前言

从学习数据结构开始就接触各种算法基础,但是自从应付完考试之后就再也没有练习过,当在开发的时候也是什么时候使用什么时候去查一下,现在在学习JavaScript,趁这个时间再把各种基础算法整理一遍,分别以JS和PHP语法的方式编写代码。

冒泡排序

原理:临近的数字两两进行比较,按照从小到大或者从大到小的顺序进行交换,这样一趟过去后,最大或最小的数字被交换到了最后一位(因为较大的/较小的 总是交换到了右边,当到最后一位时,他肯定是最大的/最小的),然后再从头开始进行两两比较交换,直到倒数第二位时结束
时间复杂度:平均情况:O(n2) 最好情况:O(n) 最坏情况:O(n2)
空间复杂度:O(1)
稳定性:稳定

       //JavaScript语法
       var array = [23,0,32,45,56,75,43,0,34];

       for(var i = 0; i < array.length; i++)
       {
           var isSort = true;
           for(var j = 0; j < array.length - 1 - i; j++)
           {
               if(array[j] > array[j+1])
               {
                   isSort = false;
                   var temp = array[j];
                   array[j] = array[j + 1];
                   array[j + 1] = temp;
               }
           }
           //如果一次交换都没有产生,就说明数据已经是排过序的,可以直接退出。
           if(isSort)
           {
               break;
           }
       }
       console.log(array);    
 <?php
         $array = [23,0,32,45,56,75,43,0,34];
 
         for($i = 0; $i < count($array); $i++)
         {
             $isSort = true;
             for($j = 0; $j < count($array) - 1; $j++)
             {
                 if($array[$j] > $array[$j+1])
                 {
                     $isSort = false;
                     $temp = $array[$j];
                     $array[$j] = $array[$j + 1];
                     $array[$j + 1] = $temp;
                 }
             }
             if($isSort)
             {
                break;
             }
         }
         var_dump($array);
 ?>

简单选择排序

原理:通过n-i次关键字之间的比较,从n-i+1 个记录中选择关键字最小的记录,并和第i(1<=i<=n)个记录交换。简单说就是分成左右两堆,左堆排过序的,右堆未排序的,从未排序的右堆中找出最小的,放到已排序的左边,形成新的两堆,直到最后排序完成。
简单选择排序的性能要略优于冒泡排序
时间复杂度:平均情况:O(n2) 最好情况:O(n) 最坏情况:O(n2)
空间复杂度:O(1)
稳定性:不稳定

//JavaScript
        var array = [23,0,32,45,56,75,43,0,34];

        for(var i = 0; i < array.length - 1; i++)
        {
            var pos = i;
            for(var j = i + 1; j < array.length;j++)
            {
                if(array[j] < array[pos])
                {
                    pos=j;
                }
            }
            var temp=array[i];
            array[i]=array[pos];
            array[pos]=temp;
        }
        console.log(array);
 ```

 ```php
 <?php
         $array = [23,0,32,45,56,75,43,0,34];
         for($i = 0; $i < count($array); $i++)
     {
         $pos = $i;
         for($j = $i + 1;$j < count($array); $j++)
         {
             if($array[$j] < $array[$pos])
             {
                 $pos = $j;
             }
         }
         $temp = $array[$i];
         $array[$i] = $array[$pos];
         $array[$pos] = $temp;
     }
     var_dump($array);
 
 ?>

直接插入排序

原理:将一个记录插入到已排序好的有序表中,从而得到一个新的记录数增1的有序表。即:先将序列的第1个记录看成是一个有序的子序列,然后从第2个记录逐个进行插入,直至整个序列有序为止。
这个和平时打牌很像,左边是已排好序的,右边是未排序的。从右边第一个时,一个个拿,拿到后插入到左边已排序的正确位置上,直到排完。
比冒泡法和选择排序的性能要更好一些。
时间复杂度:平均情况:O(n2) 最好情况:O(n) 最坏情况:O(n2)
空间复杂度:O(1)
稳定性:稳定

//JavaScript
var array = [23,0,32,45,56,75,43,0,34];
 for(var j = 0;j < array.length;j++) {
     var key = array[j];
     var i = j - 1;
     while (i > -1 && array[i] > key)
     {
         array[i + 1] = array[i];
         i = i - 1;
     }
     array[i + 1] = key;
 }
 console.log(array);
<?php
    //直接插入排序
        $array = [23,0,32,45,56,75,43,0,34];
        for($i = 0; $i < count($array); $i++)
    {
        $key = $array[$i];
        $j= $i - 1;
        while($j > -1 && $array[$j] > $key)
        {
            $array[$j +1] = $array[$j];
            $j = $j - 1;
        }
        $array[$j + 1] = $key;
    }
    var_dump($array);
?> 

快速排序

原理:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。实际开发中用得最多的
时间复杂度:平均情况:O(nlog2n) 最好情况:O(nlog2n) 最坏情况:O(n2)
空间复杂度:O(nlog2n)
稳定性:不稳定

 //JavaScript 快速排序

 var array = [23,0,32,45,56,75,43,0,34];
 var quickSort = function(arr) {
    if (arr.length <= 1) { return arr; }//检查数组的元素个数,如果小于等于1,就返回。
    var pivotIndex = Math.floor(arr.length / 2);//
    var pivot = arr.splice(pivotIndex,1)[0];//选择"基准"(pivot),并将其与原数组分离,
    var left = [];//定义两个空数组,用来存放一左一右的两个子集
    var right = [];
    for (var i = 0; i < arr.length; i++)//遍历数组,小于"基准"的元素放入左边的子集,大于基准的元素放入右边的子集。
      {
          if (arr[i] < pivot) {
              left.push(arr[i]);
          } else {
              right.push(arr[i]);
          }
      }
 
      return quickSort(left).concat([pivot], quickSort(right));//使用递归不断重复这个过程,就可以得到排序后的数组。
  };
  var newArray=quickSort(array);
  console.log(newArray);
 <?php
                $array = [23,0,32,45,56,75,43,0,34];
         function quick_sort($arr) {
             //先判断是否需要继续进行
             $length = count($arr);
             if($length <= 1) {
                 return $arr;
             }
         
             $base_num = $arr[0];//选择一个标尺  选择第一个元素
 
             //初始化两个数组
             $left_array = array();//小于标尺的
             $right_array = array();//大于标尺的
             for($i=1; $i<$length; $i++) {            //遍历 除了标尺外的所有元素,按照大小关系放入两个数组内
                 if($base_num > $arr[$i]) {
                     //放入左边数组
                     $left_array[] = $arr[$i];
                 } else {
                     //放入右边
                     $right_array[] = $arr[$i];
                 }
             }
             //再分别对 左边 和 右边的数组进行相同的排序处理方式
             //递归调用这个函数,并记录结果
             $left_array = quick_sort($left_array);
             $right_array = quick_sort($right_array);
             //合并左边 标尺 右边
             return array_merge($left_array, array($base_num), $right_array);
         }
                $newArray=quick_sort($array);
                var_dump($newArray);
 ?>

希尔排序

原理:先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行依次直接插入排序。。
时间复杂度:平均情况:O(n√n) 最好情况:O(nlog2n) 最坏情况:O(n2)
空间复杂度:O(1)
稳定性:不稳定

javaScript  希尔排序
 var array = [23,0,32,45,56,75,43,0,34];
 var shellSort = function (arr)
 {
     var length=arr.length;
     var h=1;
     while(h<length/3)
     {
         h=3*h+1;//设置间隔
     }
     while(h>=1)
     {
         for(var i=h; i<length; i++)
         {
             for(var j=i; j>=h && arr[j]<arr[j-h]; j-=h)
             {
                 var temp =arr[j-h];
                 arr[j-h]=arr[j];
                 arr[j]=temp;
             }
         }
         h=(h-1)/3;
     }
     return arr;
 }
 var newArray = shellSort(array);
 console.log(newArray);
 <?php
 //希尔排序
         $array = [23,0,32,45,56,75,43,0,34];
         function shellSort($arr)
         {
             $length=count($arr);
             $h=1;
             while($h<$length/3)
             {
                 $h=3*$h+1;//设置间隔
             }
             while($h>=1)
             {
                 for($i=$h; $i<$length; $i++)
                 {
                     for($j=$i; $j>=$h && $arr[$j]<$arr[$j-$h]; $j-=$h)
                     {
                          $temp =$arr[$j-$h];
                          $arr[$j-$h]=$arr[$j];
                          $arr[$j]=$temp;
                     }
                 }
                 $h=($h-1)/3;
             }
             return $arr;
         }
         $newArray = shellSort($array);
         var_dump($newArray)
 ?>

归并排序

原理:假设初始序列含有n个记录,则可以看成n个有序的子序列,每个子序列的长度为1,然后两两归并,得到(不小于n/2的最小整数)个长度为2或1的有序子序列,再两两归并,…如此重复,直至得到一个长度为n的有序序列为止
时间复杂度:平均情况:O(nlog2n) 最好情况:O(nlog2n) 最坏情况:O(nlog2n)
空间复杂度:O(1)
稳定性:稳定

 //JavaScript 归并排序
         function isArray1(arr){
             if(Object.prototype.toString.call(arr) =='[object Array]'){
                 return true;
             }else{
                 return false;
             }
         }
         function merge(left,right){
             var result=[];
             if(!isArray1(left)){
                 left = [left];
             }
             if(!isArray1(right)){
                 right = [right];
             }
             while(left.length > 0&& right.length >0){
                 if(left[0]<right[0]){
                     result.push(left.shift());
                 }else{
                     result.push(right.shift());
                 }
             }
             return result.concat(left).concat(right);
         }
 
         function mergeSort(arr){
             var len=arr.length;
             var lim ,work=[];
             var i,j,k;
             if(len ==1){
                 return arr;
             }
             for(i=0;i<len;i++){
                 work.push(arr[i]);
             }
             work.push([]);
             for(lim=len;lim>1;){//lim为分组长度
                 for(j=0,k=0;k<lim;j++,k=k+2){
                     work[j]=merge(work[k],work[k+1]);
                 }
                 work[j]=[];
                 lim=Math.floor((lim+1)/2);
             }
             return work[0];
         }
         var array = [23,0,32,45,56,75,43,0,34];
         
         console.log(mergeSort(array));
<?php  
      //归并排序
       function mergeSort(&$arr) {
            $len = count($arr);//求得数组长度
         
            mSort($arr, 0, $len-1);
        }
        //实际实现归并排序的程序
        function mSort(&$arr, $left, $right) {
         
            if($left < $right) {
                //说明子序列内存在多余1个的元素,那么需要拆分,分别排序,合并
                //计算拆分的位置,长度/2 去整
                $center = floor(($left+$right) / 2);
                //递归调用对左边进行再次排序:
                mSort($arr, $left, $center);
                //递归调用对右边进行再次排序
                mSort($arr, $center+1, $right);
                //合并排序结果
                mergeArray($arr, $left, $center, $right);
            }
        }

        //将两个有序数组合并成一个有序数组
        function mergeArray(&$arr, $left, $center, $right) {
            //设置两个起始位置标记
            $a_i = $left;
            $b_i = $center+1;
            while($a_i<=$center && $b_i<=$right) {
                //当数组A和数组B都没有越界时
                if($arr[$a_i] < $arr[$b_i]) {
                    $temp[] = $arr[$a_i++];
                } else {
                    $temp[] = $arr[$b_i++];
                }
            }
            //判断 数组A内的元素是否都用完了,没有的话将其全部插入到C数组内:
            while($a_i <= $center) {
                $temp[] = $arr[$a_i++];
            }
            //判断 数组B内的元素是否都用完了,没有的话将其全部插入到C数组内:
            while($b_i <= $right) {
                $temp[] = $arr[$b_i++];
            }
         
            //将$arrC内排序好的部分,写入到$arr内:
            for($i=0, $len=count($temp); $i<$len; $i++) {
                $arr[$left+$i] = $temp[$i];
            }
         
        }

        $arr = array(23,0,32,45,56,75,43,0,34);
        mergeSort($arr);
        var_dump($arr);
?>

堆排序

原理:堆排序就是利用堆进行排序的方法.基本思想是:将待排序的序列构造成一个大顶堆.此时,整个序列的最大值就是堆顶 的根结点.将它移走(其实就是将其与堆数组的末尾元素交换, 此时末尾元素就是最大值),然后将剩余的n-1个序列重新构造成一个堆,这样就会得到n个元素的次大值.如此反复执行,便��得到一个有序序列了
时间复杂度:平均情况:O(nlog2n) 最好情况:O(nlog2n) 最坏情况:O(nlog2n)
空间复杂度:O(1)
稳定性:不稳定

//JavaScript  堆排序    
      var array = [23,0,32,45,56,75,43,0,34];
       function heapSort(array)
       {
           for (var i = Math.floor(array.length / 2); i >= 0; i--)
           {
               heapAdjust(array, i, array.length - 1); //将数组array构建成一个大顶堆
           }
           for (i = array.length - 1; i >= 0; i--)
           {
               /*把根节点交换出去*/
               var temp = array[i];
               array[i] = array[0];
               array[0] = temp;
               /*余下的数组继续构建成大顶堆*/
               heapAdjust(array, 0, i - 1);
           }
           return array;
       }

       function heapAdjust(array, start, max)
       {
           var temp = array[start];//temp是根节点的值
           for (var j = 2 * start; j < max; j *= 2)
           {
               if (j < max && array[j] < array[j + 1])
               {  //取得较大孩子的下标
                   ++j;
               }
               if (temp >= array[j])
                   break;
               array[start] = array[j];
               start = j;
           }
           array[start] = temp;
       }
       var newArray = heapSort(array);
       console.log(newArray);
 
<?php
    //堆排序
    function heapSort(&$arr) {
        #初始化大顶堆
        initHeap($arr, 0, count($arr) - 1);
        
        #开始交换首尾节点,并每次减少一个末尾节点再调整堆,直到剩下一个元素
        for($end = count($arr) - 1; $end > 0; $end--) {
            $temp = $arr[0];
            $arr[0] = $arr[$end];
            $arr[$end] = $temp;
            ajustNodes($arr, 0, $end - 1);
        }
    }
    
    #初始化最大堆,从最后一个非叶子节点开始,最后一个非叶子节点编号为 数组长度/2 向下取整
    function initHeap(&$arr) {
        $len = count($arr);
        for($start = floor($len / 2) - 1; $start >= 0; $start--) {
            ajustNodes($arr, $start, $len - 1);
        }
    }
    
    #调整节点
    #@param $arr    待调整数组
    #@param $start    调整的父节点坐标
    #@param $end    待调整数组结束节点坐标
    function ajustNodes(&$arr, $start, $end) {
        $maxInx = $start;
        $len = $end + 1;    #待调整部分长度
        $leftChildInx = ($start + 1) * 2 - 1;    #左孩子坐标
        $rightChildInx = ($start + 1) * 2;    #右孩子坐标
        
        #如果待调整部分有左孩子
        if($leftChildInx + 1 <= $len) {
            #获取最小节点坐标
            if($arr[$maxInx] < $arr[$leftChildInx]) {
                $maxInx = $leftChildInx;
            }
            
            #如果待调整部分有右子节点
            if($rightChildInx + 1 <= $len) {
                if($arr[$maxInx] < $arr[$rightChildInx]) {
                    $maxInx = $rightChildInx;
                }
            }
        }
        
        #交换父节点和最大节点
        if($start != $maxInx) {
            $temp = $arr[$start];
            $arr[$start] = $arr[$maxInx];
            $arr[$maxInx] = $temp;
            
            #如果交换后的子节点还有子节点,继续调整
            if(($maxInx + 1) * 2 <= $len) {
                ajustNodes($arr, $maxInx, $end);
            }
        }
    }
    
    $arr = array(23,0,32,45,56,75,43,0,34);
    heapSort($arr);
    var_dump($arr);
?>        

基数排序

原理:将整数按位数切割成不同的数字,然后按每个位数分别比较。由于整数也可以表达字符串(比如名字或日期)和特定格式的浮点数,所以基数排序也不是只能使用于整数。
时间复杂度:平均情况:O(d(r+n)) 最好情况:O(d(n+rd)) 最坏情况:O(d(r+n)) r:关键字的基数 d:长度 n:关键字个数
空间复杂度:O(rd+n)
稳定性:稳定

<?php
      #基数排序,此处仅对正整数进行排序,至于负数和浮点数,需要用到补码,各位有兴趣自行研究
      
      #计数排序
      #@param $arr 待排序数组
      #@param $digit_num 根据第几位数进行排序
      function counting_sort(&$arr, $digit_num = false) {
          if ($digit_num !== false) { #如果参数$digit_num不为空,则根据元素的第$digit_num位数进行排序
              for ($i = 0; $i < count($arr); $i++) {
                  $arr_temp[$i] = get_specific_digit($arr[$i], $digit_num);
              } 
          } else {
              $arr_temp = $arr;
          }
  
          $max = max($arr);
          $time_arr = array(); #储存元素出现次数的数组
  
          #初始化出现次数数组
          for ($i = 0; $i <= $max; $i++) {
              $time_arr[$i] = 0;
          }
  
          #统计每个元素出现次数
          for ($i = 0; $i < count($arr_temp); $i++) {
              $time_arr[$arr_temp[$i]]++;
          }
  
          #统计每个元素比其小或相等的元素出现次数
          for ($i = 0; $i < count($time_arr) - 1; $i++) {
              $time_arr[$i + 1] += $time_arr[$i];
          }
  
          #利用出现次数对数组进行排序
          for($i = count($arr) - 1; $i >= 0; $i--) {
              $sorted_arr[$time_arr[$arr_temp[$i]] - 1] = $arr[$i];
              $time_arr[$arr_temp[$i]]--;
          }
  
          $arr = $sorted_arr;
          ksort($arr);    #忽略这次对key排序的效率损耗
      }
  
      #计算某个数的位数
      function get_digit($number) {
         $i = 1;
         while ($number >= pow(10, $i)) {
            $i++;
         }

         return $i;
      }
  
      #获取某个数字的从个位算起的第i位数
      function get_specific_digit($num, $i) {
         if ($num < pow(10, $i - 1)) {
             return 0;
         }
         return floor($num % pow(10, $i) / pow(10, $i - 1));
      }
  
      #基数排序,以计数排序作为子排序过程
      function radix_sort(&$arr) {
          #先求出数组中最大的位数
          $max = max($arr);
          $max_digit = get_digit($max);
  
          for ($i = 1; $i <= $max_digit; $i++) {
              counting_sort($arr, $i);
          }   
      }
  
  
      $arr = array(23,0,32,45,56,75,43,0,34);
      radix_sort($arr);
  
      var_dump($arr);
?>    

php curl模拟post请求提交数据例子总结

导读

在php中要模拟post请求数据提交我们会使用到curl函数,下面我来给大家举几个curl模拟post请求提交数据例子,有需要的朋友可参考参考。
注意:curl函数在php中默认是不被支持的,如果需要使用curl函数我们需在改一改你的php.ini文件的设置,找到php_curl.dll去掉前面的”;”就行了

情景

  1. 简单post数据
<?php
$uri = "http://the_site_to_test/test.php";
// 参数数组
$data = array (
        'name' => 'youname' 
// 'password' => 'password'
);
 
$ch = curl_init ();
curl_setopt ( $ch, CURLOPT_URL, $uri );//地址
curl_setopt ( $ch, CURLOPT_POST, 1 );//请求方式为post
curl_setopt ( $ch, CURLOPT_HEADER, 0 );//不打印header信息
curl_setopt ( $ch, CURLOPT_RETURNTRANSFER, 1 );//返回结果转成字符串
curl_setopt ( $ch, CURLOPT_POSTFIELDS, $data );//post传输的数据。
$return = curl_exec ( $ch );
curl_close ( $ch );
 
print_r($return);

接受php页面远程服务器:

<?php
if(isset($_POST['name'])){
    if(!empty($_POST['name'])){
        echo '您好,',$_POST['name'].'!';
    }
}
?>

2.用CURL模拟POST请求抓取邮编与地址,需要cookie做身份认证。

完整代码:

<?php
$runtime = new runtime ();
$runtime->start ();
$cookie_jar = tempnam('/tmp','cookie');
$filename = $argv[1];
$start_num= $argv[2];
$end_num  = $argv[3];

for($i=$start_num; $i<$end_num; $i++){
    $zip = sprintf('6s',$i);
    $fields_post = array(
            'postcode' => $zip, 
            'queryKind' => 2, 
            'reqCode' => 'gotoSearch', 
            'search_button.x'=>37,
            'search_button.y'=>12);


    $fields_string = http_build_query ( $fields_post, '&' );
    
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, "URL?reqCode=gotoSearch&queryKind=2&postcode=".$zip);
    curl_setopt($ch, CURLOPT_HEADER, true);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 120 );
    curl_setopt($ch, CURLOPT_REFERER, $refer );//有些网站会检查refer来屏蔽到程序的请求。所有需要模拟一个合法的。
    curl_setopt($ch, CURLOPT_HTTPHEADER, $headers_login );
    curl_setopt($ch, CURLOPT_COOKIEFILE, $cookie_jar );
    curl_setopt($ch, CURLOPT_COOKIEJAR, $cookie_jar );
    curl_setopt($ch, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']); 
    curl_setopt($ch, CURLOPT_POST, 1); // 发送一个常规的Post请求 
    curl_setopt($ch, CURLOPT_POSTFIELDS, $fields_string );


    $data = curl_exec($ch);
    
    preg_match_all('/id="table1">[s]*?<tr>[s]*?<td class="maintext">[sS]*?</td>[s]*?</tr>/', $data, $matches);
    if (!$handle = fopen($filename, 'a+')) {
        echo "不能打开文件 $filename";
        echo "n";
        exit;
    }


    if (fwrite($handle, $matches[0][1]) === FALSE) {
        echo "不能写入到文件 $filename";
        echo "n";
        exit;
    }


    echo "成功地将 $somecontent 写入到文件$filename";
    echo "n";


    fclose($handle);
    curl_close($ch);
}




class runtime
{
    var $StartTime = 0;
    var $StopTime = 0;
    function get_microtime()
    {
        list($usec,$sec)=explode(' ',microtime());return((float)$usec+(float)$sec);
    }
    function start()
    {
        $this->StartTime=$this->get_microtime();
    }
    function stop(){
        $this->StopTime=$this->get_microtime();
    }
    function spent()
    {
        return ($this->StopTime-$this->StartTime);
    }
}




$runtime->stop ();


$con = 'Processed in'.$runtime->spent().'seconds';
echo 'Processed in'. $runtime->spent().'seconds';

3.模拟POST请求 提交数据或上传文件 .

代码如下

http://www.a.com/a.php 发送POST请求

function execUpload(){


$file = '/doucment/Readme.txt';
$ch = curl_init();
$post_data = array(
    'loginfield' => 'username',
    'username' => 'ybb',
    'password' => '123456',
'file' => '@d:usrwwwtranslatedocumentReadme.txt'
);
curl_setopt($ch, CURLOPT_HEADER, false);
//启用时会发送一个常规的POST请求,类型为:application/x-www-form-urlencoded,就像表单提交的一样。
curl_setopt($ch, CURLOPT_POST, true);  
curl_setopt($ch,CURLOPT_BINARYTRANSFER,true);
curl_setopt($ch, CURLOPT_POSTFIELDS,$post_data);
curl_setopt($ch, CURLOPT_URL, 'http://www.b.com/handleUpload.php');
$info= curl_exec($ch);
curl_close($ch);
   
print_r($info);

}

2.http://www.b.com/handleUpload.php

function handleUpload(){
    print_r($_POST);
    echo '===file upload info:';
    print_r($_FILES);
}

4. post json数据

$data = array("name" => "Hagrid", "age" => "36");
$data_string = json_encode($data);

$ch = curl_init('http://api.local/rest/users');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_POSTFIELDS,$data_string);
curl_setopt($ch, CURLOPT_RETURNTRANSFER,true);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
    'Content-Type: application/json',
    'Content-Length: ' . strlen($data_string))
);
 
$result = curl_exec($ch);

 

■cURL 函数

■curl_close — 关闭一个cURL会话
■curl_copy_handle — 复制一个cURL句柄和它的所有选项
■curl_errno — 返回最后一次的错误号
■curl_error — 返回一个保护当前会话最近一次错误的字符串
■curl_exec — 执行一个cURL会话
■curl_getinfo — 获取一个cURL连接资源句柄的信息
■curl_init — 初始化一个cURL会话
■curl_multi_add_handle — 向curl批处理会话中添加单独的curl句柄
■curl_multi_close — 关闭一组cURL句柄
■curl_multi_exec — 运行当前 cURL 句柄的子连接
■curl_multi_getcontent — 如果设置了CURLOPT_RETURNTRANSFER,则返回获取的输出的文本流
■curl_multi_info_read — 获取当前解析的cURL的相关传输信息
■curl_multi_init — 返回一个新cURL批处理句柄
■curl_multi_remove_handle — 移除curl批处理句柄资源中的某个句柄资源
■curl_multi_select — 等待所有cURL批处理中的活动连接
■curl_setopt_array — 为cURL传输会话批量设置选项
■curl_setopt — 设置一个cURL传输选项
■curl_version — 获取cURL版本信息

总结

结合这篇和通过php写wordpress博客就可以抓取别人的文章 然后自动发博客。当然这里面其实还少了一讲,正则表达式。不过 可以用php-readability,git上就有源码。

mac上建议安装Dash,看各种文档很方便。

如何开发一个WordPress插件

介绍

WordPress 插件 允许你对 WordPress 博客进行修改、自定义和加强。不必修改 WordPress 的核心程序,直接用插件的形式增加功能。下面是对 WordPress 插件的基本定义:

WordPress 插件:WordPress 插件是用 PHP 语言写成的一只或者一组程序。这些程序可以为 WordPress 增加某些原来没有的功能,这样使用者看起来仿佛就是这个博客固有的功能。 插件 API

想为你的博客添加功能吗?那么最简单的方法就是搜搜看有没有现成的插件。如果很不幸——没有,那么这篇文章会指导你自己开发一个。

本文假设你已经熟悉 WordPress 的基本功能,以及 PHP 编程。

资源

  • 插件资源集合 有各种你可能需要的资源,包括外站关于写插件的文章,以及特定主题的文章。
  • 学习一个叫 Hello Dolly 的插件“范本”可以领你入门。
  • 如果你的插件已经写完了,并自以为写的不错,查看 插件提交以及推广

新建一个插件

这个部分告诉你怎么把开发插件的理想变为现实。

名称,文件和地方

插件名

你得先想一个名字,并且努力让它独一无二。在 Plugins 或者其他宝贝地方——Google或者百度先验证一下这个名字到底是不是独一无二的。另外你的名字得让别人明白你的插件是干什么的。

插件文件

下一步是创建一个PHP文件。按照原文奇怪的逻辑,你得先想好名字。这个名字还得是从插件名衍生过来的(其实是为你自己辨认的)。举个例子吧,比如说你的插件名字叫 “Fabulous Functionality”,你的PHP名字可能是 fabfunc.php。另外不要用汉语拼音(这也是我加的),还要避免重名。人民群众会把你的插件安装到一个你也知道的叫wp-content/plugins/的地方,如果名字冲突岂不要悲剧了。

你也可以选择把插件分割成几个文件。 显而易见一个php文件是必需的,同时还需要图片、CSS、JavaScript、语言(当然也可以没有)。如果有很多文件,命名一个php和一个文件夹,例如 fabfunc and fabfunc.php。把你所有插件文件放到文件夹里,然后让你的用户相信只要把你的整个压缩包解压到 wp-content/plugins/就能正常使用你的劳动成果。

在本文的其余部分,“插件的PHP文件”是指主要插件的PHP文件,无论是在的wp-content/plugins/或子目录。

Readme文件

如果你想将你的插件发布到http://wordpress.org/extend/plugins/, 你必须在插件包中建立一个标准格式readme.txt文件. 文件格式参见http://wordpress.org/extend/plugins/about/readme.txt.

主页

最好为插件建立一个主页,以介绍插件的功能、安装方法、使用说明、适用的WordPress版本、插件更新信息等。

文件Headers

现在开始吧,首先让我们从向PHP主文件中加入一些信息

标准插件信息

插件的主文件顶部必须包括一个标准插件信息头。WordPress通过标准信息头识别插件的存在,并把她加入到控制面板的插件管理页面,这样插件才能激活,载入插件,并运行里面的函数;如果没有信息头,插件将无法激活和使用。标准信息插件头的格式为:

1
2
3
4
5
6
7
8
9
10
11
<?php
/*
Plugin Name: 插件名
Plugin URI: 插件的介绍或更新地址
Description: 插件描述
Version: 插件版本,例如 1.0
Author: 插件作者名称
Author URI: 插件作者的链接
License: A "Slug" license name e.g. GPL2
*/
?>

标准信息头至少要包括插件名称,这样WordPress才能识别你的插件。其他信息将显示在控制面板插件管理页面中。标准插件信息对各行顺序没有要求。

这样的升级机制能够正确地读出你的插件版本,建议你选择一个格式的版本号,不同版本之间,并坚持下去。例如,x.x中或x.x.x或xx.xx.xxx

注意:文件必须是 UTF-8 格式!

版权信息

通常我们还要在标准信息头中加入插件的许可证信息。大多数插件使用GPLGPLCompatibleLicenses许可。如果使用GPL许可,要求插件中包含以下信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
/*  Copyright 年份  作者名  (email : 你的邮箱)
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.
 
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
 
    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/
?>

开始编写插件

现在是时候让你的插件能干些什么了。这部分内容包括插件开发的一般思路,而且介绍了开发插件需要做哪些工作。

WordPress插件钩子

许多WordPress插件通过连接一个或多个WordPress插件钩子来完成他们的功能。插件钩子的运行机理是,当WordPress运行到不同阶段,WordPress会检查当前阶段是否注册了插件函数,如果是,那么函数将被执行。通过执行插件函数改变了WordPress的默认功能。

例如,在WordPress将日志标题和post发送到浏览器前,会检查是否有插件函数注册了名为“the_title”的“filter”钩子。如果是,标题文本将会传送到注册函数中,注册函数返回值将会传送到浏览器。所以,如果要在日志标题中加入一些信息,可以通过这种方式实现。

另一个例子是名为“wp_footer”的“action”钩子。在WordPress的HTML页脚创建之前,会检查是否有插件注册了名为“wp_footer”的“action”钩子,如果是依次执行她们。

Plugin API了解更多如何注册“filter”和“action”类型的钩子函数,及WordPress提供了那些插件钩子。如果你发现WordPress没有提供自己想要的钩子,你可以建议WordPress加入这个钩子,很多建议WordPress都会采纳。具体方法参考Reporting Bugs

模版标签

另一个通过插件加入新功能的方法是建立自定义的模版标签Template Tags。如果有人想用你的插件,可以在他们的主题中添加这些标签,边栏,文章内容段,或者任意的只要是适合这插件的地方。例如,一个给文章添加地理位置的插件可能定义了一个模板标签函数geotag_list_states()放在边栏上,这里列表了所有在文章中关联的州的名称,并且还带有插件提供的到这些州的文档页的链接。

定义一个自定义模板标签,仅需要写一个PHP函数,并且在你插件主页或者插件的主PHP文件中声明一下。声明函数的时候,为这个函数提供一个示例来明确如果想要应用这个函数需要加主题中加些什么文件是个相当棒的主意。

保存插件数据到数据库

大多数WordPress插件需要站点的所有者或者是博客的用户输入信息,然后在对话过程中保存起来,以便过滤器函数(filter)、动作函数(action)或者模板函数(Template)使用。这些信息必须保存在WordPress的数据库中,以便下次使用。这里有两种基本的方法用于保存插件的信息到数据库里面。

  1. 使用WordPress的”option”机制(稍后会有介绍)。这种方法适合于保存一些相对小数量的静态命名类数据–这类数据通常只需要网站的所有者在首次建立插件的时候输入,以后很少改动。
  2. 文章属性post meta(a.k.a. Custom Fields),适用于那些只和个人文章、页面或者附件有关的数据。
  3. 参看post_meta Function Examples, add_post_meta(), 以及与文章相关函数属性post.fuction meta (a.k.a. Custom Fields).
  4. 自定义分类法。对于文章分类或者其他对象,比如用户、评论,或者用户可编辑列表中的数据名称/值,可以考虑使用自定义分类法,尤其是当你要访问的所有的文章/对象与给定的分类法项目相关联的时候。查阅 Custom Taxonomies
  5. 在数据库中创建一个新的自定义数据表。这种方法适合于与个人文章、页面、附件或者评论相关的数据,这类数据会随着时间越来越多,它们也没有专有名称。参看Creating Tables with Plugins来了解更多这类信息处理方法。

WordPress的选项机制

参看 Creating Options Pages 得到更多如何创建会自动保存你选项数据的页面。

WordPress拥有一个机制来保存,更新和检索WordPress数据库中专用,名称类数据(即”options”机制)。选项值可以是字符,数组或PHP对象(他们会被”序列化”,或在存储前转换为字符,并在被检索时解开序列)。选项名称是字符,并且它们必须是独一无二的,这样就不会与其它的WordPress插件相冲突。

它也通常被认为是一个不错的主意,将你的插件使用的选项的数量降到最低。例如,考虑存储序列化数组的10个元素作为一个单一的命名选项,而不是存储10个不同的命名选项。

这里是你的插件应用WordPress option功能的主要函数。

1
add_option($name, $value, $deprecated, $autoload);
建立一个新的option; 如果这个option已经存在则不做动作.
$name
必须 (string). 要添加的option的名称.
$value
可选(string), 默认是空字符. option值会存在这里.
$deprecated
可选 (string), 不再被WordPress使用了,你可以不填或NULL 如果你希望应用后面的$autoload参数.
$autoload
可选, 默认为 ‘yes’ (enum: ‘yes’ or ‘no’). 如果设置为 ‘yes’ 那么这个option会被get_alloptions 函数自动检索.
1
get_option($option);
在数据库中检索option值.
$option
Required (string). 你想返回数值的option名称。你可以Option Reference在找到一个随着WordPress一起安装好的默认option表。
1
update_option($option_name, $newvalue);
更新或创建数据库中的option值(注意 add_option 不是必须被调用,如果你不想作用 $deprecated 或$autoload 参数).
$option_name
必须(string). 要更新的option名.
$newvalue
必须. (string|array|object) option的新值.

管理面板

假定你的插件有一些选项(option)存储于WordPress的数据库中(参看上一节),你可能会想要一个主控面板来允许你的插件用户查看和编辑选项值。实现这一目标的方法阐述于Adding Administration Menus

插件国际化

在你完成了你的插件的编写工作之后,另一个需要考虑的问题(假设你准备跟大家分享你的插件的话)就是将其国际化。国际化就是将你的软件设置成能够本地化的过程;本地化是将软件中显示的语言翻译成其他语言的过程。Wordpress正在被全球的人们使用,所以全球化和本地化是他内在的特性,这其中就包括了插件的本地化。

请注意,插件的语言文件是不会自动加载。将此插件代码,以确保加载的语言文件:

1
load_plugin_textdomain('your-unique-name', false, basename( dirname( __FILE__ ) ) . '/languages' );

要简单地取一个字符串使用 __(‘String name’,’your-unique-name’); 返回翻译或者 _e(‘String name’,’your-unique-name’); 输出翻译。翻译,然后进入你插件的  /languages  文件夹。

我们十分希望你能够将你的插件国际化,这样其他国家的用户就可以在自己的本地使用它了。我们有一个关于国际化的综合说明在I18n for WordPress Developers,这其中就包括了一个描述插件国际化的部分。

更新你的插件

本节介绍将插件托管到 http://wordpress.org/extend/plugins 之后必要的更新步骤。特别列出wordpress.org关于使用 Subversion(SVN)的一些细节。

假设你已经提交你的插件到WordPress的插件库,随着时间的推移,你可能会发现需要将某些功能添加到插件或修正错误。更新代码,并将变化提交到你的插件主干(trunk),这些变化将是公开可见的,但仅限于在技术上志同道合的人通过SVN检查你的插件。其他用户通过网站或自己的WordPress插件管理下载都不会改变。

当你准备发布一个新版本的插件:

  • 确保一切承诺和新版本的实际工作。注意所有版本的WordPress的插件支持,并尝试与他们进行测试。不要只是测试新功能,也确保你不小心打破一些插件的旧功能。
  • 更改主要的PHP文件头注释中的版本号为新的版本号。
  • 更改readme.txt文件的“Stable tag”字段中的版本号。
  • 在readme.txt文件中添加一个新的小节“changelog“,简要介绍与最后一个版本相比,新版本有什么改变。这将列出的插件页面上的“更新日志”选项卡。
  • 提交这些更改。
  • 创建一个新的SVN标记作为副本主干(trunk),遵循 this guide

给系统一个运行两三分钟,然后检查你的插件,看看更新是否一切正常,以及WordPress 是否提示插件有更新(更新检查可能有缓存,比如wordpress.org插件页面或后台安装,所以这可能需要一些时间 —— 尝试访问“可用更新”页面)。

故障排除:

  • wordpress.org插件的页面上仍然列出旧版本。你是否更新了树干文件夹’stable tag’ 字段?只创建一个标签和或更新readme.txt文件是不够的!
  • 插件的页面提供了一个zip文件的新版本,但按钮仍然列出旧的版本号,而且WordPress 没有装更新通知。你是否已修改主要的PHP文件中“Version”版本号?
  • 对于其他问题,请参考: The Plugins directory and readme.txt files

插件开发建议

最后这个部分是关于开发插件的一些建议。

  • WordPress插件的代码应该遵循 WordPress Coding Standards. 另外请同时参考Inline Documentation
  • 你的插件中所有函数的名称都应该与现存的Wordpress Core函数,其他插件或主题的任何名称不同。基于这个原因,我们建议你在你的插件的所有函数的名称之前加上一个你自己选择的前缀,或者把你的插件的函数都写在一个类里面(当然这个类的名字也必须是唯一的)。
  • 请不要把Wordpress数据库表格前缀(通常是“wp_”)直接写在你的插件里,请使用$wpdb->prefix 。
  • 虽然数据库的读取相对便宜,但它的写入是相当昂贵的。数据库十分擅长获取信息并呈现给用户,而且这些操作(通常)是非常迅速的。然而对数据库进行改动就是一个非常复杂的过程了,而且需要使用更长的计算时间。因此,请尽量减少你对数据库进行写入的次数。在你编写程序的时候就做好所有的准备,这样就可以只在必须的时候再进行写入了。
  • 在数据库里只SELECT你需要的东西。尽管数据库的读取十分便捷,我们依然推荐你值查找真正需要的数据,来尽量减少数据库的负载。例如,如果你只想获得表格的行数,不要使用 SELECT * FROM, 因为这样的话每一行中的所有数据都会被读出,导致内存的浪费。同样的,如果在插件中你只想获得post_id和post_author,请只 SELECT 这两项来减少数据库的负载。记住:在某一个操作的同时可能有其他上百个进程需要使用数据库,而数据库和服务器都必须同时满足所有这些进程的需求。学习怎样尽量减少你的插件对数据库的使用可以避免对这些资源的滥用。
  • 不要让你的PHP出错。在你的wp_config.php文件中添加define(‘WP_DEBUG’,true);,对你的所有函数进行测试来确定是否有任何的错误或者警告。有多少,就修复多少,直到再也不出现为止。
  • 尽量不要直接调用<script>和<style>标记 —— 推荐使用 wp_enqueue_style() 和 wp_enqueue_script() 函数。他们帮助消除引用重复的脚本和样式,以及引进依赖的支持。

 

  • 原文:http://codex.wordpress.org/Writing_a_Plugin
  • 编译:倡萌@WordPress大学 (参考官方中文文档)