鲍成龙
到底是,为什么呢?
鲍成龙

刀是冷的、剑是冷的、心是冷的,但是一壶热酒下肚,孤傲乖戾也能化作一片赤心。

鲍成龙

缓冲(buffer)是为了协调吞吐速度相差很大的设备之间数据传送而采用的技术,用来存放缓冲数据的区域叫缓冲区,在计算机科学领域,当数据从一个地方传送到另一个地方时,缓冲区被用来临时存储数据。与缓冲相似的一个技术是缓存(cache),它们都是为了解决数据存储和传输速度不同而带来的问题,不同的是,缓冲主要在写时使用,而缓存主要用来在读时使用。

PHP输出缓冲及其应用

如上图,是一个简易的缓冲区模拟图,左端入口的数据具有单个输入体积小,速度快,数量多,但右端输出数据具有体积大,速度慢的特点。如果没有缓冲区,很容易造成数据堵塞,有了缓冲区之后,当数据填满缓冲区,再统一输出,则可以大大减少系统负担。

PHP在执行的过程中,嵌入的HTML代码,’echo’,’print_r’等指令都是一次数据输出,正是因为有缓冲区的存在,系统可以在php执行完之后再一次把数据发送给浏览器,运行如下代码:

<?php
echo "这里是第一行数据";
echo "这里是第二行数据,下面睡眠5秒";
sleep(5);
echo "这里是第三行数据,下面是HTML代码";
?>
<h1>标题</h1>

发现浏览器是同时显示所有内容,而不是先显示第一行和第二行数据,等待5秒后再显示后面的数据。不仅这样,PHP的缓冲区还提供给我们更加强大的功能,我们可以在数据发送之前对其作出捕获,更改等。PHP提供给我们”ob_”系列函数,例如如下代码,可以对某些字符进行替换:

<?php
ob_start();
echo "Hello world, this is http://www.hitoy.org/";
$content = ob_get_contents();
ob_end_clean();
echo str_replace("http://","https://",$content);
?>

上面中的ob_start,ob_get_contents,ob_end_clean分别用来开启用户缓冲区,获取缓存内容和关闭缓存区,PHP中所有的输出控制函数有1:
flush — 刷新输出系统缓冲
ob_clean — 清空(擦掉)输出缓冲区
ob_end_clean — 清空(擦除)缓冲区并关闭输出缓冲
ob_end_flush — 冲刷出(送出)输出缓冲区内容并关闭缓冲
ob_flush — 冲刷出(送出)输出缓冲区中的内容
ob_get_clean — 得到当前缓冲区的内容并删除当前输出缓。
ob_get_contents — 返回输出缓冲区的内容
ob_get_flush — 刷出(送出)缓冲区内容,以字符串形式返回内容,并关闭输出缓冲区。
ob_get_length — 返回输出缓冲区内容的长度
ob_get_level — 返回输出缓冲机制的嵌套级别
ob_get_status — 得到所有输出缓冲区的状态
ob_gzhandler — 在ob_start中使用的用来压缩输出缓冲区中内容的回调函数。ob_start callback function to gzip output buffer
ob_implicit_flush — 打开/关闭绝对刷送
ob_list_handlers — 列出所有使用中的输出处理程序。
ob_start — 打开输出控制缓冲
output_add_rewrite_var — 添加URL重写器的值(Add URL rewriter values)
output_reset_rewrite_vars — 重设URL重写器的值(Reset URL rewriter values)

php中,可以通过php.ini的output_buffering来设置缓存,On表示无穷大,Off表示关闭,数字则表示缓冲区的大小(以字节为单位),默认大小是4KB,如果设置成off,则示例一的代码是不是就可以分段在浏览器显示了呢?答案是否定的,有两点需要注意,第一点即使把PHP的缓存关闭,php输出在系统层面也有缓存(可以理解为Linux系统stdout的缓存),必须通过flush函数输出;第二点是一些有些浏览器对一次接收的文字长度有限制,如果太少,则不予显示。所以这样的代码可以分段显示:

<?php
echo "这里是第一行数据";
echo str_repeat("&nbsp;",1024);
echo "这里是第二行数据,下面睡眠5秒";
flush();
sleep(5);
echo "这里是第三行数据,下面是HTML代码";
?>
<h1>标题</h1>

当然在实际生产环境,直接把output_buffering关闭的情况比较少见,我们可以通过ob_系列函数来进行操作,下面的例子是利用缓冲进行服务器推送(comet)的示例。如下代码可以给客户端进行信息推送:

<?php
ob_start();
$i=0;
while($i<100){
    echo $i.str_repeat("&nbsp;",2024);
    $i++;
    ob_flush();
    flush();
    sleep(5);
}
?>
鲍成龙

在WordPress中,所有的ajax请求都通过admin-ajax.php接管,只需要后台简单的添加动作,即可实现ajax请求。

创建函数

ajax分为管理员权限和普通用户权限,统一以wp_ajax开头,首先,在插件或者主题functions.php文件中,创建一个函数,例如:

function fun_hello_world(){
    echo '大家好,我是渣渣灰';
    wp_die();//停止内容
}

添加钩子

add_action('wp_ajax_hello_world', 'fun_hello_world');//管理员调用 
add_action('wp_ajax_nopriv_hello_world', 'fun_hello_world');//未登录用户可以调用

其中,第一个为钩子名称,wp_ajax_后面的内容,你自己起,例如这儿以hello_world结尾。第二个参数为函数名称,我绑定了上面创建的这个函数。
那么,在管理员权限下,可以调用第一个动作,非管理员,可以调用第二个动作。调用第一个,如果没有登录,调用的话则为400错误。

调用方法

使用jquery。必须包含参数action,这个就是上面注册的名称了。

var data = {action: 'hello_world'}
$.post("<?php echo admin_url('admin-ajax.php');?>", data, function (data) {
     //获取内容回调
});
鲍成龙

引号

正确的使用单引号和双引号,如果字符串中不包含变量的时候,则使用单引号,永远不要在字符串中转移引号,而是通过切换引号类型,比如:

echo '<a href="/static/link" title="Yeah yeah!">Link name</a>';
echo "<a href='$link' title='$linktitle'>$linkname</a>";

HTML 或 XML 属性中的文本应该进行转义,以便单引号或者双引号不会结束属性使得 HTML 标签无效,甚至引起安全问题,如何对属性进行转义,我们会在以后的文章中详细讲解。

 

缩进

首先使用制表符而不是空格进行缩进,并且使用空格把代码对齐,以便更易阅读:

$foo   = 'somevalue';
    $foo2  = 'somevalue2';
    $foo34 = 'somevalue3';
    $foo5  = 'somevalue4';

对于关联数组,如果数据含有多个元素的时候,每个元素都应该新起一行:

$query = new WP_Query( array( 'ID' => 123 ) );
$args = array(
    'post_type'   => 'page',
    'post_author' => 123,
    'post_status' => 'publish',
);
 
$query = new WP_Query( $args );

特别关注一下数组最后一个元素后面的逗号,推荐都加上,因为这样更容易调整数组的顺序,并且更容易添加新的元素,因为不用关注之前最后一个是否有逗号。

对于 switch 结构, case 语句应该比 switch 语句多缩进一个制表符, case 的内容也要比 case 条件语句缩进一个 tab。

switch ( $type ) {
    case 'foo':
        some_function();
        break;
    case 'bar':
        some_function();
        break;
}

经验法则:行首缩进使用制表符,行中对齐使用空格。

大括号

大括号的使用样式如下所示:

if ( condition ) {
    action1();
    action2();
} elseif ( condition2 && condition3 ) {
    action3();
    action4();
} else {
    defaultaction();
}

如果代码块非常长,那就要考虑将其分解成更多的较短的代码块,函数或者方法,以便降低复杂度,提高测试的便利性,当然也增加了代码的可读性。

不管是否必需,都要使用大括号:

if ( condition ) {
    action0();
}
 
if ( condition ) {
    action1();
} elseif ( condition2 ) {
    action2a();
    action2b();
}
 
foreach ( $items as $item ) {
    process_item( $item );
}

特别注意,强制使用大括号意味着禁止单语句内联控制结构,但是可以使用控制结构的替代语法(例如 if/endifwhile/endwhile)——尤其是在 HTML 的模板中嵌入 PHP 代码的时候 ,例如:

<?php if ( have_posts() ) : ?>
    <div class="hfeed">
        <?php while ( have_posts() ) : the_post(); ?>
            <article id="post-<?php the_ID() ?>" class="<?php post_class() ?>">
                <!-- ... -->
            </article>
        <?php endwhile; ?>
    </div>
<?php endif; ?>

使用 elseif 而不是 else if

因为 else if 和 if|elseif 代码块的冒号语法不兼容,因此条件语句中使用 elseif

数组声明

使用长数组语法 ( array( 1, 2, 3 ) ) 声明数组通常比短数组语法 ( [ 1, 2, 3 ] ) 更具有可读性,对于初学者,也更有描述性。

所以数组声明必须使用长数组语法。

闭包(匿名函数)

在一些的情况下(比如回调函数只需要用一次),可以使用闭包而非重写一个新函数来作为回调函数传递,比如:

$caption = preg_replace_callback(
    '/<[a-zA-Z0-9]+(?: [^<>]+>)*/',
    function ( $matches ) {
        return preg_replace( '/[\r\n\t]+/', ' ', $matches[0] );
    },
    $caption
);

但是不建议 filter 或 action 的回调函数使用闭包,因为通过 remove_action() / remove_filter() 移除的时候,就变得复杂了。

函数多行调用

将一个函数调用拆分为多行时,每个参数必须位于单独的行上, 单行内联注释可以单独一行:每个参数不得超过一行,如果一个参数需要多行,那么可以先将其赋值给一个变量,然后再将该变量传递给函数调用。

$bar = array(
    'use_this' => true,
    'meta_key' => 'field_name',
);
$baz = sprintf(
    /* translators: %s: Friend's name */
    esc_html__( 'Hello, %s!', 'yourtextdomain' ),
    $friend_name
);
 
$a = foo(
    $bar,
    $baz,
    /* translators: %s: cat */
    sprintf( __( 'The best pet is a %s.' ), 'cat' )
);

正则表达式

正则表达式应该使用 Perl 兼容的正则表达式(PCRE, preg_函数),另外永远不要使用 /e 开关,而是使用 preg_replace_callback。在正则表达式中使用单引号字符串是最简便的,因为相比双引号,单引号字符串只有两个元序列需要转移:\' 和 \\

PHP 开始和结束标记

在 HTML 模板中如果要嵌入多行 PHP 代码时,PHP 开始和结束标记都要自己单独一行。

正确(多行):

function foo() {
    ?>
    <div>
        <?php
        echo bar(
            $baz,
            $bat
        );
        ?>
    </div>
    <?php
}

正确(单行):

<input name="<?php echo esc_attr( $name ); ?>" />

错误:

if ( $a === $b ) { ?><some html><?php }

不要使用简写的 PHP 标记

重要:永远不要使用简写的 PHP 标记,使用完整版。

正确:

<?php ... ?>
<?php echo $var; ?>

错误:

<? ... ?>
<?= $var ?>

删除行尾的空格

删除每行末尾的空格,最好在文件末尾省略 PHP 结束标记,如果没有省略,那就确保删除 PHP 结束标记后面的空格。

空格的用法

始终在逗号后放置空格,并在逻辑运算符、比较运算符、字符串连接符和赋值运算符的两侧放置空格。

x === 23
foo && bar
! foo
array( 1, 2, 3 )
$baz . '-5'
$term .= 'X'

在控制语句中的左括号和右括号的两侧防止空格:

foreach ( $foo as $bar ) { ...

定义函数时,这样使用空格:

function my_function( $param1 = 'foo', $param2 = 'bar' ) { ...
function my_other_function() { ...

调用函数时:

my_function( $param1, func_param( $param2 ) );
my_other_function();

当执行逻辑运算时:

if ( ! $foo ) { ...

类型转换必须使用小写的, 并且使用简短形式,(int)  而不是 (integer) ,(bool)  而不是 (boolean),对于浮点类型转换,请使用 (float) 而不是 (real),因为 (real) 在 PHP 7.4 已被弃用,并在 PHP 8 被移除。

foreach ( (array) $foo as $bar ) { ...
$foo = (bool) $bar;

当涉及到数组元素的时候,仅当元素的索引是变量的时候,在索引周围包含空格,例如:

$x = $foo['bar']; // 正确
$x = $foo[ 'bar' ]; // 错误
 
$x = $foo[0]; // 正确
$x = $foo[ 0 ]; // 错误
 
$x = $foo[ $bar ]; // 正确
$x = $foo[$bar]; // 错误

在 switch 代码中,  case 提交和冒号之间不要有空格:

switch ( $foo ) {
    case 'bar': // 正确
    case 'ba' : // 错误
}

同样,返回的类型声明的冒号前不应有空格:

function sum( $a, $b ): float {
    return $a + $b;
}

除非另有说明,括号内应有空格。

if ( $foo && ( $bar || $baz ) ) { ...
my_function( ( $x - 1 ) * 5, $y );

格式化 SQL 语句

在格式化 SQL 语句时,如果 SQL 很复杂,可以将 SQL 语句分成几行并缩进。当然大部分 SQL 语句一行就可以了。然后将 SQL 语句中的关键字(比如 UPDATE 或者 WHERE)大写。需要更新数据库的函数的参数,传递来之前应该没有对数据进行 SQL 斜杠转义,转义应该尽可能接近查询的时候执行,并且最好使用  $wpdb->prepare() 进行。$wpdb->prepare() 是一种处理 SQL 查询的转义、引用和整数转换的方法。它使用 sprintf() 格式的子集。例子 :$wpdb->prepare() 是用来对 SQL 查询进行转义、引用和整数转换等操作的方法,它 sprintf() 的一部分格式化方法,比如:

$var = "dangerous'"; // 可能未转义的原始数据
$id = some_foo_number(); // 期待是整形的数据,但是不能确定
 
$wpdb->query( $wpdb->prepare( "UPDATE $wpdb->posts SET post_title = %s WHERE ID = %d", $var, $id ) );

%s 用于字符串占位符,而 %d 用于整数占位符。注意他们没有被引用,$wpdb->prepare() 会执行转义和引用的工作。

数据库查询

避免直接操作数据库,如果有定义的函数可以获取你需要的数据,则使用它。数据库抽象(使用函数而不是查询)有助于保持代码向前兼容,并且在查询结果被缓存到内存中的时候,它可以快很多倍。

命名约定

在变量名,action/filter 的名称和函数名使用小写,不要使用驼峰式,通过下划线分割单词, 如非必需不要使用缩写,让代码无歧义并能自我说明:

function some_name( $some_variable ) { [...] }

类名必须首字母大写,并用下划线分割,首字母缩写词都应全部大写:

class Walker_Category extends Walker { [...] }
class WP_HTTP { [...] }

常量必须全部大写,并用下划线分割

define( 'DOING_AJAX', true );

文件名应使用小写字母进行描述性命名,使用连字符应分隔:

my-plugin-name.php

类文件名应该基于类名,然后在前面加上 class-,然后类名中的下划线替换为连字符,例如 WP_Error 的文件名:

class-wp-error.php

在 wp-includes 目录中含有函数模板标签函数的文件,都会在文件名称末尾附加 -template 以便它们显而易见。

general-template.php

每个文件只有一个对象结构(类/接口/特征)

比如,有个名为 class-example-class.php 的文件,它只包含一个类:
// 错误: 文件 class-example-class.php
class Example_Class { [...] }
class Example_Class_Extended { [...] }

第二个类应该在自己的名字为  class-example-class-extended.php 的文件中:

// 正确:文件 class-example-class-extended.php.
class Example_Class_Extended { [...] }
// 正确:文件 class-example-class-extended.php.
class Example_Class_Extended { [...] }

函数参数的自解释标志值

调用函数时使用字符串值而不是 true 和 false

// 错误
function eat( $what, $slowly = true ) {
...
}
eat( 'mushrooms' );
eat( 'mushrooms', true ); // true 是什么意思呢?
eat( 'dogfood', false ); // false 又是什么意思呢?false 的反面?

PHP 直到 8.0 起,才支持命名参数,但是,由于 WordPress 目前仍支持较旧的 PHP 版本,我们还不能使用这些版本。

没有命名参数,标志的值是没有意义的,每次遇到像上面例子这样的函数调用时,我们都必须搜索函数定义才明白什么意思。通过使用描述性字符串值而不是布尔值,可以使代码更具可读性。

// 正确
function eat( $what, $speed = 'slowly' ) {
...
}
eat( 'mushrooms' );
eat( 'mushrooms', 'slowly' );
eat( 'dogfood', 'quickly' );

当需要使用更多词汇来描述函数参数时,$args 数组参数可能是更好的模式:

// 更好
function eat( $what, $args ) {
...
}
eat ( 'noodles', array( 'speed' => 'moderate' ) );

动态 Hook 的插值命名

出于可读性和可发现性的目的,应使用插值而不是串联来命名动态 Hook。动态 Hook 是在其标签名中包含动态值的 Hook,例如 {$new_status}_{$post->post_type}(publish_post)。HooK 标签中使用的变量应该用大括号 { 和 } 括起来,完整的外部标签名称用双引号括起来。这是为了确保 PHP 可以正确解析内插字符串中给定的变量。
do_action( "{$new_status}_{$post->post_type}", $post->ID, $post );

可能的话,标签名称中的动态值也应尽可能简洁明了,比如 $user_id 就比 $this->id 更加清晰明了。

三元运算符

使用三元运算符很好,但是尽量让他们先测试为真,而不是假,否则容易混淆(一个例外是使用 ! empty(), 因为这里为假反而更直观)。
// (if statement is true) ? (do this) : (else, do this);
$musictype = ( 'jazz' === $music ) ? 'cool' : 'blah';
// (if field is not empty ) ? (do this) : (else, do this);

尤达表达式

if ( true === $the_force ) {
    $victorious = you_will( $be );
}

在涉及变量的逻辑比较时,始终将变量放在右侧,将常量、文字或函数调用放在左侧。如果双方都不是变量,则顺序并不重要。(在计算机科学术语中,在比较中总是尝试将 l 值放在右侧,将 r 值放在左侧。)

在上面的例子中,如果省略了一个等号(承认吧,即使是我们当中最有经验的人也会犯这种错误),你会得到一个解析错误,因为你不能赋值给一个像 true 这样的常量,如果该语句是相反的( $the_force = true ),则赋值将完全有效,返回 1,导致 if 语句结果为 true,这种错误可能让你花费很长时间去 Debug。

尤达表达式适用于  ==!====, 和 !==。而 <><= 或者 >=  情况不会出现赋值的可能,并且不易阅读,不建议使用。

“聪明”的代码

一般来说,代码的可读性比聪明和简洁更重要:
isset( $var) || $var= some_function();

虽然上面的代码很巧妙,但如果你不熟悉它,需要一段时间才能理解。所以还是这样写吧:

if ( ! isset( $var ) ) {
    $var = some_function();
}

除非绝对的必要,否则不应使用松散的比较,因为可能会产生误导。

正确:

if ( 0 === strpos( 'WordPress', 'foo' ) ) {
    echo __( 'Yay WordPress!' );
}

错误:

if ( 0 == strpos( 'WordPress', 'foo' ) ) {
    echo __( 'Yay WordPress!' );}

赋值最好不要在条件表达式中:

正确:

$data = $wpdb->get_var( '...' );
if ( $data ) {

    // Use $data
}

错误:

if ( $data = $wpdb->get_var( '...' ) ) {
    // Use $data
}

在 switch 语句中,可以将多个空的 case 放到一起。但是,如果一个 case 包含代码,然后直接进入下一个代码块,则必须明确注释。

switch ( $foo ) {
    case 'bar':       // 正确,空的 case 无需注释
    case 'baz':
        echo $foo;    // 错误, 含有代码的 case 必须 break,return 或者含有注释
    case 'cat':
        echo 'mouse';
        break;        // 正确,有 break 的 case 无需注释
    case 'dog':
        echo 'horse';
        // no break   // 正确,含有注释的 case 可以不要 break。
    case 'fish':
        echo 'bird';
        break;
}

goto 语句绝对不能用。

eval() 是非常危险并且无法确保安全。 create_function() 函数,相当于内部执行了内部执行 eval(),PHP 7.2 起已弃用,并已在 PHP 8.0 中删除,所以都不能使用。

错误控制符 @

引用 PHP 文档:
[alert class="info"]PHP 支持一种错误控制运算符:at 符号 (@)。当附加到 PHP 中的表达式时,该表达式可能生成的任何诊断错误都将被抑制。[/alert]

虽然在 WordPress 核心代码中确实存在此运算符,但它经常被懒惰地使用,而不是进行适当的错误检查。强烈建议不要使用它,甚至 PHP 文档也指出:

[alert class="info"]警告:在 PHP 8.0.0 之前,@ 运算符可以禁用将终止脚本执行的严重错误。例如,将 @ 附加到不存在的函数调用之前,由于不可用或输入错误,将导致脚本终止而没有说明原因。[/alert]

不要使用 extract()

extract() 是一个非常糟糕的函数,它使代码更难调试和更难理解,我们应该不应该使用它,并删除现有代码中的所有使用。

 

鲍成龙

序列化处理是 WordPress 的强项,比如在使用 update_option 的时候,可以把字符串,数组,或者对象直接存进去,WordPress 会自动对非标量进行序列化处理,需要用到的时候,只需要使用 get_option 函数取出数据即可,原本是数组,取出来还是数组,原来是对象,取出来依然是对象,非常方便。

WordPress 是怎么做到的呢?这个就是 WordPress 自己定义了几个序列化处理的相关函数把序列化处理过程做的更简单:

maybe_unserialize 和 maybe_serialize 函数

首先 WordPress 扩展了 PHP 原生的 unserialize 和 serialize 函数,定义了 maybe_unserialize 和 maybe_serialize 函数。

maybe_unserialize( $string );
maybe_serialize( $data );

maybe_unserialize 是 WordPress 提供的反序列化函数,官方的解释是:Unserialize value only if it was serialized. 相比 PHP 的 unserialize 函数,它会首先会检测传递进来的字符串是不是序列化之后的字符串,是的话,它才使用 PHP 的 unserialize 函数进行反序列化,如果不是,则直接返回。所以它的名字会有个 maybe

maybe_serialize 是 WordPress 的序列化函数,如果参数 $data 是普通字符串,则直接返回,如果 $data 是对象或者数组,则使用 PHP serialize 函数对齐进行序列化。

特别需要注意的是,如果 $data 是已经序列化的字符串,函数还会对其再进行一次序列化操作。

下面的例子比较全面解释其用法:

// 字符串不做任何处理,直接返回。
$data = 'Hello World!';
echo maybe_serialize( $data );
// Hello World!

// 整形,浮点型,和布尔型数据也是不做处理,直接返回。
$data = 55;
echo maybe_serialize( $data );
// 55

$data = 4.560
echo maybe_serialize( $data );
// 4.560

$data = true;
$data = maybe_serialize( $data );
// $data = true;

$data = null;
$data = maybe_serialize( $data );
// $data = null

// 对象和数组会被转换成序列化数据
$data = array( 1 => 'Hello World!', 'foo' => 'bar' );
echo maybe_serialize( $data );
// a:2:{i:1;s:12:"Hello World!";s:3:"foo";s:3:"bar";}

// 已经序列化的字符串,会被再次序列化
$data = 'a:2:{i:1;s:12:"Hello World!";s:3:"foo";s:3:"bar";}';
echo maybe_serialize( $data );
// s:50:"a:2:{i:1;s:12:"Hello World!";s:3:"foo";s:3:"bar";}";

is_serialized 和 is_serialized_string 函数

前面提到 WordPress 会判断字符串是不是序列化的字符串,这个是怎么实现的呢?

WordPress 提供了 is_serialized 和 is_serialized_string 这两个函数用来检测当前字符串是不是序列化字符串。

is_serialized( $data );

is_serialized_string( $data );

简单说:is_serialized 用法更广泛一点,无论数组,对象,字符串被序列化之后的字符串,它判断为 true,而 is_serialized_string 只有字符串被序列化之后,才判断为 true。下面的例子也非常全面的区分两者的区别:

var_dump( is_serialized( serialize(NULL) ) );		// true
var_dump( is_serialized_string( serialize(NULL) ) );	// false

var_dump( is_serialized( serialize(array(1,2,3)) ) );		// true
var_dump( is_serialized_string( serialize(array(1,2,3)) ) );	// false

var_dump( is_serialized( serialize(123) ) );		// true
var_dump( is_serialized_string( serialize(123) ) );	// false

var_dump( is_serialized( serialize(123) ) );		// true
var_dump( is_serialized_string( serialize(123) ) );	// false

 

鲍成龙

代码解决办法:

register_taxonomy(
       'travel-category', //taxonomy base
       'destination', // custom post type
       'rewrite' => array( 'slug' => '/', 'with_front' => FALSE )
);

 

如果出现404,在 设置 – 固定链接 中重新保存即可。

 

鲍成龙
$("button").dblclick(function(){
  $("p").slideToggle();
});

定义和用法

当双击元素时,会发生 dblclick 事件。

当鼠标指针停留在元素上方,然后按下并松开鼠标左键时,就会发生一次 click。

在很短的时间内发生两次 click,即是一次 double click 事件。

dblclick() 方法触发 dblclick 事件,或规定当发生 dblclick 事件时运行的函数。

提示:如果把 dblclick 和 click 事件应用于同一元素,可能会产生问题。

鲍成龙
$("p").on("taphold",function(){
  $(this).hide();
});

 

定义和用法
taphold 事件轻击并不放(大约一秒)后触发。

$("selector").on("taphold",function(event){...})

 

参数 描述
function(event) 必须。指定 swipe 事件触发时执行的函数。

该函数有可选的事件对象 ,事件对象可以是任何 jQuery 事件属性 (例如 event.target, event.type, 等)。

鲍成龙
<?php
echo "今天:".date("Y-m-d")."<br>";     
echo "昨天:".date("Y-m-d",strtotime("-1 day")), "<br>";     
echo "明天:".date("Y-m-d",strtotime("+1 day")). "<br>";  
echo "一周后:".date("Y-m-d",strtotime("+1 week")). "<br>";     
echo "一周零两天四小时两秒后:".date("Y-m-d G:H:s",strtotime("+1 week 2 days 4 hours 2 seconds")). "<br>";     
echo "下个星期四:".date("Y-m-d",strtotime("next Thursday")). "<br>";     
echo "上个周一:".date("Y-m-d",strtotime("last Monday"))."<br>";     
echo "一个月前:".date("Y-m-d",strtotime("last month"))."<br>";     
echo "一个月后:".date("Y-m-d",strtotime("+1 month"))."<br>";     
echo "十年后:".date("Y-m-d",strtotime("+10 year"))."<br>";    
?>

strtotime()函数的作用是将日期时间描述解析为 Unix 时间戳

int strtotime ( string time [, int now] )

PHP星期几获取代码:

 

  1. date("l");
  2. //data就可以获取英文的星期比如Sunday
  3. date("w");
  4. //这个可以获取数字星期比如123,注意0是星期日

获取中文星期可以这样

  1. $weekarray=array("日","一","二","三","四","五","六");
  2. echo "星期".$weekarray[date("w")];

获取指定日期是:

  1. $weekarray=array("日","一","二","三","四","五","六");
  2. echo "星期".$weekarray[date("w","2011-11-11")];

因为date函数非常强大,他完全可以胜任一切这样的工作我附个手册里的表吧

  1. a - "am" 或是 "pm"
  2. A - "AM" 或是 "PM"
  3. d - 几日,二位数字,若不足二位则前面补零; 如: "01" 至 "31"
  4. D - 星期几,三个英文字母; 如: "Fri"
  5. F - 月份,英文全名; 如: "January"
  6. h - 12 小时制的小时; 如: "01" 至 "12"
  7. H - 24 小时制的小时; 如: "00" 至 "23"
  8. g - 12 小时制的小时,不足二位不补零; 如: "1" 至 12"
  9. G - 24 小时制的小时,不足二位不补零; 如: "0" 至 "23"
  10. i - 分钟; 如: "00" 至 "59"
  11. j - 几日,二位数字,若不足二位不补零; 如: "1" 至 "31"
  12. l - 星期几,英文全名; 如: "Friday"
  13. m - 月份,二位数字,若不足二位则在前面补零; 如: "01" 至 "12"
  14. n - 月份,二位数字,若不足二位则不补零; 如: "1" 至 "12"
  15. M - 月份,三个英文字母; 如: "Jan"
  16. s - 秒; 如: "00" 至 "59"
  17. S - 字尾加英文序数,二个英文字母; 如: "th","nd"
  18. t - 指定月份的天数; 如: "28" 至 "31"
  19. U - 总秒数
  20. w - 数字型的星期几,如: "0" (星期日) 至 "6" (星期六)
  21. Y - 年,四位数字; 如: "1999"
  22. y - 年,二位数字; 如: "99"
  23. z - 一年中的第几天; 如: "0" 至 "365"

鲍成龙

我们知道 Linux 服务器有个 Cron 的功能,可以用来设置定时执行的作业,但是并不是每个人都熟悉 Linux 系统,并且也不是所有的主机管理面板都有 Cron 栏目。

其实 WordPress 本身也有类似于 Cron 的功能,让我们可以直接在 WordPress 中定义和执行定时作业,WordPress 把这个功能定义为:WP-Cron,比如 WordPress 本身的文章预发布功能就是基于 WP-Cron 实现的。

WP-Cron 功能是基于页面浏览的,所以时间上不会十分准确,但是随着站点流量增大,这个准确度会越来越高的。WP-Cron 定义了一套完整的 API,让 WordPress 第三方开发者可以通过插件的去定义定时作业。

 

定义作业

WP-Cron 支持两种类型的定义作业:

单一的未来事件(比如设定某篇文章在将来某个时间发布)
重复发生的事件,比如每天或每个星期等一段时间内重复发生的事件(比如定时清理无用的信息)

 

定义单一的未来事件

我们可以使用函数

wp_schedule_single_event($timestamp, $hook, $args)

来定义单一的未来事件,它有三个参数:

$timestamp:事件发生的时间戳。

$hook:事件调用的 hook,需要预先通过 add_action 关联定时作业的回调函数。

$args:传给回调函数的参数数组。

定义重复发生的事件

我们可以使用函数

wp_schedule_event($timestamp, $recurrence, $hook, $args)

来定义重复发生的时间。

$timestamp,$hook 和 $args 这三个参数含义和 wp_schedule_single_event 的一样。

$recurrence:事件重复的频率。

定时作业的频率

WordPress 已经内置了四种重复频率:

频率 描述
hourly 每小时1次
twicedaily 每天2次 -- 12小时1次
daily 每天1次 -- 24小时1次
weekly 每周1次

 

但是如果还不够用,比如每15分钟1次,甚至频率更高一些,每5分钟1次。WP-Cron 也支持自定义事件频率类型。

查看 wp_get_schedules() 函数的源代码,频率内部是使用秒数来定义的,然后我们可以通过 cron_schedule 这个 filter 去自定义频率:

add_filter('cron_schedules', unction($schedules){
	return array_merge($schedules, [
		'five_minutes'		=> ['interval'=>300,	'display'=>'每5分钟一次'],
		'fifteen_minutes'	=> ['interval'=>900,	'display'=>'每15分钟一次'],
	]);
});

 

定时作业的 hook 和回调函数

从上面可知,WordPress 的定时作业对应就是一个 hook,然后通过 hook 关联具体的回调函数,比如我们要定义一个每天都要执行的作业,我们把这个作业 hook 定义为:daily_function_hook,具体的回调函数为:daily_function。

function daily_function(){
	// 把每天定时执行的作业具体的代码写在这里
}

把回调函数关联到 hook 上:

add_action( 'daily_function_hook', 'daily_function');

将 hook 加入到定时作业列表,有两种方法:

1. 代码的方式:

if(!wp_next_scheduled('daily_function_hook')){
	wp_schedule_event(time(), 'daily', 'daily_function_hook');
}

上面代码,首先使用函数 wp_next_scheduled 检测 daily_function_hook 是否已经被加入到定时作业列表中,如果没有,我们使用 wp_schedule_event 函数加入,这样可以确保作业只被定义一次。

 

 

 

鲍成龙

WordPress 提供了一个,timer_stop()函数,官方给出的介绍为:检索或显示从页面开始到调用函数的时间。简单的说就是我们访问一个 WordPress 链接,服务器生成处理这个页面所需要的时间,当然这并不是指我们打开或者加载这个页面的时间。更多的是服务器端的处理速度和 WordPress 本身运行情况。

//函数
timer_stop( $display, $precision = 3 )
 
//$display:是否直接输出,int | bool 必选,是否回显或返回结果。0 或 false 表示返回;1 或 true 表示输出显示。默认值为 0 | false。
 
//$precision:精度,int 可选,指要显示的小数点右边的位数。默认为 3.
 
//示例
 
<?php timer_stop(1);//直接输出显示 ?>

最后分享一个比较常见的关于 WordPress 显示当前页面数据库查询次数、页面生成时间以及服务器内存消耗情况的方法代码,如下:

<?php echo '<!-- ' . get_num_queries() . ' queries in ' . timer_stop(0,3) . ' seconds ' . memory_get_peak_usage()/1024/1024 . 'MB memory -->';?>

 

鲍成龙

一、ajax嵌套

ajax嵌套ajax。跟疯狂for循环一样,这种方式只能串行发起ajax请求。弊端也很明显,随着ajax请求次数增加,嵌套层数也逐渐加深,代码很难维护。而且,效率也不高。

function getData(){
    $.ajax({url: "/something",type:'GET',dataType:'json',success: function(data1){
            $.ajax({url: "/something",type:'GET',dataType:'json',success: function(data2){
                    $.ajax({url: "/something",type:'GET',dataType:'json',success: function(data3){
                            console.log(data1);
                            console.log(data2);
                            console.log(data3);
                        }
                    })
                }
            })
        }
    })
}

二、设置一个全局变量

所有ajax接口都可以同时发起请求,而且代码分离,在每个ajax的callback都统一回调一个函数,由这个函数来判断全局变量是否达到条件。

function getData(){
    var flag = 0,
        _data1,
        _data2,
        _data3;
    $.ajax({url: "/something",type:'GET',dataType:'json',success: function(data1){
            flag++;
            _data1 = data1;
            if(flag == 3){
                //do something
            }
        }
    })
 
    $.ajax({url: "/something",type:'GET',dataType:'json',success: function(data2){
            flag++;
            _data2 = data2;
 
            if(flag == 3){
                //do something
            }
        }
    })
 
    $.ajax({url: "/something",type:'GET',dataType:'json',success: function(data3){
            flag++;
            _data3 = data3;
            if(flag == 3){
                //do something
            }
        }
    })
}

三、使用jquery的$.when()方法,就是promise模式,让异步请求方式变为链式调用,以及能做到并行请求

如果所有请求成功返回,then方法里第一个回调执行,如果任意一个请求失败,则执行第二个回调。

function getData(){
    var fun1 = $.ajax({url: "/something",type:'GET',dataType:'json'}),
        fun2 = $.ajax({url: "/something",type:'GET',dataType:'json'}),
        fun3 = $.ajax({url: "/something",type:'GET',dataType:'json'});
    $.when(fun1,fun2,fun3).then(function(data1,data2,data3){
        //成功回调,所有请求正确返回时调用
        console.log(data1);
        console.log(data2);
        console.log(data3);
    },function(){
        //失败回调,任意一个请求失败时返回
        console.log('error');
    })  
}