当前位置:  编程技术>php
本页文章导读:
    ▪php获取$_POST同名参数数组的实现介绍       今天写php的时候发现$_POST["arr"]无法获取参数arr的数组,记录一下。例如有以下表单需要提交: 代码如下:  <input type="checkbox" name="arr" value="" />  <input type="checkbox" name="arr" value="" /&.........
    ▪深入解析php中的foreach问题       前言:php4中引入了foreach结构,这是一种遍历数组的简单方式。相比传统的for循环,foreach能够更加便捷的获取键值对。在php5之前,foreach仅能用于数组;php5之后,利用foreach还能遍历对象(详.........
    ▪浅析Apache中RewriteCond规则参数的详细介绍       RewriteCond就像我们程序中的if语句一样,表示如果符合某个或某几个条件则执行RewriteCond下面紧邻的RewriteRule语句,这就是RewriteCond最原始、基础的功能,为了方便理解,下面来看看几个例子.........

[1]php获取$_POST同名参数数组的实现介绍
    来源: 互联网  发布时间: 2013-11-30
今天写php的时候发现$_POST["arr"]无法获取参数arr的数组,记录一下。
例如有以下表单需要提交:
代码如下:

  <input type="checkbox" name="arr" value="" />
  <input type="checkbox" name="arr" value="" />
  <input type="checkbox" name="arr" value="" />
  <input type="checkbox" name="arr" value="" />

 使用$_POST["arr"]只能获得最后选择的复选框的值,要获得全部选中的复选框的值需要把表单修改成下面:
代码如下:

  <input type="checkbox" name="arr[]" value="" />
  <input type="checkbox" name="arr[]" value="" />
  <input type="checkbox" name="arr[]" value="" />
  <input type="checkbox" name="arr[]" value="" />

这样就可以使用$_POST["arr"]获得全部选中的checkbox的值了。

    
[2]深入解析php中的foreach问题
    来源: 互联网  发布时间: 2013-11-30
前言:
php4中引入了foreach结构,这是一种遍历数组的简单方式。相比传统的for循环,foreach能够更加便捷的获取键值对。在php5之前,foreach仅能用于数组;php5之后,利用foreach还能遍历对象(详见:遍历对象)。本文中仅讨论遍历数组的情况。

foreach虽然简单,不过它可能会出现一些意外的行为,特别是代码涉及引用的情况下。
下面列举了几种case,有助于我们进一步认清foreach的本质。
问题1:
代码如下:

$arr = array(1,2,3);
foreach($arr as $k => &$v) {
    $v = $v * 2;
}
// now $arr is array(2, 4, 6)
foreach($arr as $k => $v) {
    echo "$k", " => ", "$v";
}

先从简单的开始,如果我们尝试运行上述代码,就会发现最后输出为0=>2  1=>4  2=>4 。
为何不是0=>2  1=>4  2=>6 ?
其实,我们可以认为 foreach($arr as $k => $v) 结构隐含了如下操作,分别将数组当前的'键'和当前的'值'赋给变量$k和$v。具体展开形如:
代码如下:

foreach($arr as $k => $v){
    //在用户代码执行之前隐含了2个赋值操作
    $v = currentVal();
    $k = currentKey();
    //继续运行用户代码
    ……
}

根据上述理论,现在我们重新来分析下第一个foreach:
第1遍循环,由于$v是一个引用,因此$v = &$arr[0],$v=$v*2相当于$arr[0]*2,因此$arr变成2,2,3
第2遍循环,$v = &$arr[1],$arr变成2,4,3
第3遍循环,$v = &$arr[2],$arr变成2,4,6
随后代码进入了第二个foreach:
第1遍循环,隐含操作$v=$arr[0]被触发,由于此时$v仍然是$arr[2]的引用,即相当于$arr[2]=$arr[0],$arr变成2,4,2
第2遍循环,$v=$arr[1],即$arr[2]=$arr[1],$arr变成2,4,4
第3遍循环,$v=$arr[2],即$arr[2]=$arr[2],$arr变成2,4,4
OK,分析完毕。
如何解决类似问题呢?php手册上有一段提醒:
Warning : 数组最后一个元素的 $value 引用在 foreach 循环之后仍会保留。建议使用unset()来将其销毁。
代码如下:

$arr = array(1,2,3);
foreach($arr as $k => &$v) {
    $v = $v * 2;
}
unset($v);
foreach($arr as $k => $v) {
    echo "$k", " => ", "$v";
}
// 输出 0=>2  1=>4  2=>6

从这个问题中我们可以看出,引用很有可能会伴随副作用。如果不希望无意识的修改导致数组内容变更,最好及时unset掉这些引用。
问题2:
代码如下:

$arr = array('a','b','c');
foreach($arr as $k => $v) {
    echo key($arr), "=>", current($arr);
}
// 打印 1=>b 1=>b 1=>b

这个问题更加诡异。按照手册的说法,key和current分别是取数组中当前元素的的键值。
那为何key($arr)一直是1,current($arr)一直是b呢?
先用vld查看编译之后的opcode:

我们从第3行的ASSIGN指令看起,它代表将array('a','b','c')赋值给$arr。
由于$arr为CV,array('a','b','c')为TMP,因此ASSIGN指令找到实际执行的函数为ZEND_ASSIGN_SPEC_CV_TMP_HANDLER。这里需要特别指出,CV是PHP5.1之后才增加的一种变量cache,它采用数组的形式来保存zval**,被cache住的变量再次使用时无需去查找active符号表,而是直接去CV数组中获取,由于数组访问速度远超hash表,因而可以提高效率。
代码如下:

static int ZEND_FASTCALL  ZEND_ASSIGN_SPEC_CV_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
    zend_op *opline = EX(opline);
    zend_free_op free_op2;
    zval *value = _get_zval_ptr_tmp(&opline->op2, EX(Ts), &free_op2 TSRMLS_CC);

    // CV数组中创建出$arr**指针
    zval **variable_ptr_ptr = _get_zval_ptr_ptr_cv(&opline->op1, EX(Ts), BP_VAR_W TSRMLS_CC);
    if (IS_CV == IS_VAR && !variable_ptr_ptr) {
        ……
    }
    else {
        // 将array赋值给$arr
         value = zend_assign_to_variable(variable_ptr_ptr, value, 1 TSRMLS_CC);
        if (!RETURN_VALUE_UNUSED(&opline->result)) {
            AI_SET_PTR(EX_T(opline->result.u.var).var, value);
            PZVAL_LOCK(value);
        }
    }
    ZEND_VM_NEXT_OPCODE();
}

ASSIGN指令完成之后,CV数组中被加入zval**指针,指针指向实际的array,这表示$arr已经被CV缓存了起来。

接下来执行数组的循环操作,我们来看FE_RESET指令,它对应的执行函数为ZEND_FE_RESET_SPEC_CV_HANDLER:
代码如下:

static int ZEND_FASTCALL  ZEND_FE_RESET_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
    ……
    if (……) {
        ……
    } else {
        // 通过CV数组获取指向array的指针
        array_ptr = _get_zval_ptr_cv(&opline->op1, EX(Ts), BP_VAR_R TSRMLS_CC);
        ……
    }
    ……
    // 将指向array的指针保存到zend_execute_data->Ts中(Ts用于存放代码执行期的temp_variable)
    AI_SET_PTR(EX_T(opline->result.u.var).var, array_ptr);
    PZVAL_LOCK(array_ptr);
    if (iter) {
        ……
    } else if ((fe_ht = HASH_OF(array_ptr)) != NULL) {
        // 重置数组内部指针
        zend_hash_internal_pointer_reset(fe_ht);
        if (ce) {
            ……
        }
        is_empty = zend_hash_has_more_elements(fe_ht) != SUCCESS;

        // 设置EX_T(opline->result.u.var).fe.fe_pos用于保存数组内部指针
        zend_hash_get_pointer(fe_ht, &EX_T(opline->result.u.var).fe.fe_pos);
    } else {
        ……
    }
    ……
}

这里主要将2个重要的指针存入了zend_execute_data->Ts中:
•EX_T(opline->result.u.var).var ---- 指向array的指针
•EX_T(opline->result.u.var).fe.fe_pos ---- 指向array内部元素的指针
FE_RESET指令执行完毕之后,内存中实际情况如下:



接下来我们继续查看FE_FETCH,它对应的执行函数为ZEND_FE_FETCH_SPEC_VAR_HANDLER:

代码如下:

static int ZEND_FASTCALL  ZEND_FE_FETCH_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
    zend_op *opline = EX(opline);

    // 注意指针是从EX_T(opline->op1.u.var).var.ptr获取的
    zval *array = EX_T(opline->op1.u.var).var.ptr;
    ……

    switch (zend_iterator_unwrap(array, &iter TSRMLS_CC)) {
        default:
        case ZEND_ITER_INVALID:
            ……
        case ZEND_ITER_PLAIN_OBJECT: {
            ……
        }
        case ZEND_ITER_PLAIN_ARRAY:
            fe_ht = HASH_OF(array);

            // 特别注意:
            // FE_RESET指令中将数组内部元素的指针保存在EX_T(opline->op1.u.var).fe.fe_pos
            // 此处获取该指针
            zend_hash_set_pointer(fe_ht, &EX_T(opline->op1.u.var).fe.fe_pos);

            // 获取元素的值
            if (zend_hash_get_current_data(fe_ht, (void **) &value)==FAILURE) {
                ZEND_VM_JMP(EX(op_array)->opcodes+opline->op2.u.opline_num);
            }
            if (use_key) {
                key_type = zend_hash_get_current_key_ex(fe_ht, &str_key, &str_key_len, &int_key, 1, NULL);
            }

            // 数组内部指针移动到下一个元素
            zend_hash_move_forward(fe_ht);

            // 移动之后的指针保存到EX_T(opline->op1.u.var).fe.fe_pos
            zend_hash_get_pointer(fe_ht, &EX_T(opline->op1.u.var).fe.fe_pos);
            break;
        case ZEND_ITER_OBJECT:
            ……
    }

    ……
}

根据FE_FETCH的实现,我们大致上明白了foreach($arr as $k => $v)所做的事情。它会根据zend_execute_data->Ts的指针去获取数组元素,在获取成功之后,将该指针移动到下一个位置再重新保存。

简单来说,由于第一遍循环中FE_FETCH中已经将数组的内部指针移动到了第二个元素,所以在foreach内部调用key($arr)和current($arr)时,实际上获取的便是1和'b'。
那为何会输出3遍1=>b呢?
我们继续看第9行和第13行的SEND_REF指令,它表示将$arr参数压栈。紧接着一般会使用DO_FCALL指令去调用key和current函数。PHP并非被编译成本地机器码,因此php采用这样的opcode指令去模拟实际CPU和内存的工作方式。
查阅PHP源码中的SEND_REF:

代码如下:

static int ZEND_FASTCALL  ZEND_SEND_REF_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
    ……
    // 从CV中获取$arr指针的指针
    varptr_ptr = _get_zval_ptr_ptr_cv(&opline->op1, EX(Ts), BP_VAR_W TSRMLS_CC);
    ……

    // 变量分离,此处重新copy了一份array专门用于key函数
    SEPARATE_ZVAL_TO_MAKE_IS_REF(varptr_ptr);
    varptr = *varptr_ptr;
    Z_ADDREF_P(varptr);

    // 压栈
    zend_vm_stack_push(varptr TSRMLS_CC);
    ZEND_VM_NEXT_OPCODE();
}

上述代码中的SEPARATE_ZVAL_TO_MAKE_IS_REF是一个宏:
代码如下:

#define SEPARATE_ZVAL_TO_MAKE_IS_REF(ppzv)    \
    if (!PZVAL_IS_REF(*ppzv)) {                \
        SEPARATE_ZVAL(ppzv);                \
        Z_SET_ISREF_PP((ppzv));                \
    }

SEPARATE_ZVAL_TO_MAKE_IS_REF的主要作用为,如果变量不是一个引用,则在内存中copy出一份新的。本例中它将array('a','b','c')复制了一份。因此变量分离之后的内存为:
注意,变量分离完成之后,CV数组中的指针指向了新copy出来的数据,而通过zend_execute_data->Ts中的指针则依然可以获取旧的数据。
接下来的循环就不一一赘述了,结合上图来说:
•foreach结构使用的是下方蓝色的array,会依次遍历a,b,c
•key、current使用的是上方黄色的array,它的内部指针永远指向b
至此我们明白了为何key和current一直返回array的第二个元素,由于没有外部代码作用于copy出来的array,它的内部指针便永远不会移动。
问题3:
代码如下:

$arr = array('a','b','c');
foreach($arr as $k => &$v) {
    echo key($arr), '=>', current($arr);
}// 打印 1=>b 2=>c =>

本题与问题2仅有一点区别:本题中的foreach使用了引用。用VLD查看本题,发现与问题2代码编译出来的opcode一样。因此我们采用问题2的跟踪方法,逐步查看opcode对应的实现。
首先foreach会调用FE_RESET:
代码如下:

static int ZEND_FASTCALL  ZEND_FE_RESET_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
    ……
    if (opline->extended_value & ZEND_FE_RESET_VARIABLE) {
        // 从CV中获取变量
        array_ptr_ptr = _get_zval_ptr_ptr_cv(&opline->op1, EX(Ts), BP_VAR_R TSRMLS_CC);
        if (array_ptr_ptr == NULL || array_ptr_ptr == &EG(uninitialized_zval_ptr)) {
            ……
        }
        else if (Z_TYPE_PP(array_ptr_ptr) == IS_OBJECT) {
            ……
        }
        else {
            // 针对遍历array的情况
            if (Z_TYPE_PP(array_ptr_ptr) == IS_ARRAY) {
                SEPARATE_ZVAL_IF_NOT_REF(array_ptr_ptr);
                if (opline->extended_value & ZEND_FE_FETCH_BYREF) {
                    // 将保存array的zval设置为is_ref
                    Z_SET_ISREF_PP(array_ptr_ptr);
                }
            }
            array_ptr = *array_ptr_ptr;
            Z_ADDREF_P(array_ptr);
        }
    } else {
        ……
    }
    ……
}

问题2中已经分析了一部分FE_RESET的实现。这里需要特别注意,本例foreach获取值采用了引用,因此在执行的时候FE_RESET中会进入与上题不同的另一个分支。
最终,FE_RESET会将array的is_ref设置为true,此时内存中只有一份array的数据。
接下来分析SEND_REF:
代码如下:

static int ZEND_FASTCALL  ZEND_SEND_REF_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
    ……
    // 从CV中获取$arr指针的指针
    varptr_ptr = _get_zval_ptr_ptr_cv(&opline->op1, EX(Ts), BP_VAR_W TSRMLS_CC);
    ……

    // 变量分离,由于此时CV中的变量本身就是一个引用,此处不会copy一份新的array
    SEPARATE_ZVAL_TO_MAKE_IS_REF(varptr_ptr);
    varptr = *varptr_ptr;
    Z_ADDREF_P(varptr);

    // 压栈
    zend_vm_stack_push(varptr TSRMLS_CC);
    ZEND_VM_NEXT_OPCODE();
}

宏SEPARATE_ZVAL_TO_MAKE_IS_REF仅仅分离is_ref=false的变量。由于之前array已经被设置了is_ref=true,因此它不会被拷贝一份副本。换句话说,此时内存中依然只有一份array数据。

上图解释了前2次循环为何会输出1=>b 2=>C。在第3次循环FE_FETCH的时候,将指针继续向前移动。

代码如下:

ZEND_API int zend_hash_move_forward_ex(HashTable *ht, HashPosition *pos)
{
    HashPosition *current = pos ? pos : &ht->pInternalPointer;
    IS_CONSISTENT(ht);
    if (*current) {
        *current = (*current)->pListNext;
        return SUCCESS;
    } else
        return FAILURE;
}

由于此时内部指针已经指向了数组的最后一个元素,因此再向前移动会指向NULL。将内部指针指向NULL之后,我们再对数组调用key和current,则分别会返回NULL和false,表示调用失败,此时是echo不出字符的。
 问题4:
代码如下:

$arr = array(1, 2, 3);
$tmp = $arr;
foreach($tmp as $k => &$v){
    $v *= 2;
}
var_dump($arr, $tmp); // 打印什么?

该题与foreach关系不大,不过既然涉及到了foreach,就一起拿来讨论吧:)
代码里首先创建了数组$arr,随后将该数组赋给了$tmp,在接下来的foreach循环中,对$v进行修改会作用于数组$tmp上,但是却并不作用到$arr。
为什么呢?
这是由于在php中,赋值运算是将一个变量的值拷贝到另一个变量中,因此修改其中一个,并不会影响到另一个。
题外话:这并不适用于object类型,从PHP5起,对象的便总是默认通过引用进行赋值,举例来说:
代码如下:

class A{
    public $foo = 1;
}
$a1 = $a2 = new A;
$a1->foo=100;
echo $a2->foo; // 输出100,$a1与$a2其实为同一个对象的引用

回到题目中的代码,现在我们可以确定$tmp=$arr其实是值拷贝,整个$arr数组会被再复制一份给$tmp。理论上讲,赋值语句执行完毕之后,内存中会有2份一样的数组。
也许有同学会疑问,如果数组很大,岂不是这种操作会很慢?
幸好php有更聪明的处理办法。实际上,当$tmp=$arr执行之后,内存中依然只有一份array。查看php源码中的zend_assign_to_variable实现(摘自php5.3.26):
代码如下:

static inline zval* zend_assign_to_variable(zval **variable_ptr_ptr, zval *value, int is_tmp_var TSRMLS_DC)
{
    zval *variable_ptr = *variable_ptr_ptr;
    zval garbage;
    ……
  // 左值为object类型
    if (Z_TYPE_P(variable_ptr) == IS_OBJECT && Z_OBJ_HANDLER_P(variable_ptr, set)) {
        ……
    }
    // 左值为引用的情况
    if (PZVAL_IS_REF(variable_ptr)) {
        ……
    } else {
        // 左值refcount__gc=1的情况
        if (Z_DELREF_P(variable_ptr)==0) {
            ……
        } else {
            GC_ZVAL_CHECK_POSSIBLE_ROOT(*variable_ptr_ptr);
            // 非临时变量
            if (!is_tmp_var) {
                if (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) {
                    ALLOC_ZVAL(variable_ptr);
                    *variable_ptr_ptr = variable_ptr;
                    *variable_ptr = *value;
                    Z_SET_REFCOUNT_P(variable_ptr, 1);
                    zval_copy_ctor(variable_ptr);
                } else {
                    // $tmp=$arr会运行到这里,
                    // value为指向$arr里实际array数据的指针,variable_ptr_ptr为$tmp里指向数据指针的指针
                    // 仅仅是复制指针,并没有真正拷贝实际的数组
                    *variable_ptr_ptr = value;
                    // value的refcount__gc值+1,本例中refcount__gc为1,Z_ADDREF_P之后为2
                    Z_ADDREF_P(value);
                }
            } else {
                ……
            }
        }
        Z_UNSET_ISREF_PP(variable_ptr_ptr);
    }
    return *variable_ptr_ptr;
}

可见$tmp = $arr的本质就是将array的指针进行复制,然后将array的refcount自动加1.用图表达出此时的内存,依然只有一份array数组:

既然只有一份array,那foreach循环中修改$tmp的时候,为何$arr没有跟着改变?
继续看PHP源码中的ZEND_FE_RESET_SPEC_CV_HANDLER函数,这是一个OPCODE HANDLER,它对应的OPCODE为FE_RESET。该函数负责在foreach开始之前,将数组的内部指针指向其第一个元素。
代码如下:

static int ZEND_FASTCALL  ZEND_FE_RESET_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
    zend_op *opline = EX(opline);
    zval *array_ptr, **array_ptr_ptr;
    HashTable *fe_ht;
    zend_object_iterator *iter = NULL;
    zend_class_entry *ce = NULL;
    zend_bool is_empty = 0;
    // 对变量进行FE_RESET
    if (opline->extended_value & ZEND_FE_RESET_VARIABLE) {
        array_ptr_ptr = _get_zval_ptr_ptr_cv(&opline->op1, EX(Ts), BP_VAR_R TSRMLS_CC);
        if (array_ptr_ptr == NULL || array_ptr_ptr == &EG(uninitialized_zval_ptr)) {
            ……
        }
        // foreach一个object
        else if (Z_TYPE_PP(array_ptr_ptr) == IS_OBJECT) {
            ……
        }
        else {
            // 本例会进入该分支
            if (Z_TYPE_PP(array_ptr_ptr) == IS_ARRAY) {
                // 注意此处的SEPARATE_ZVAL_IF_NOT_REF
                // 它会重新复制一个数组出来
                // 真正分离$tmp和$arr,变成了内存中的2个数组
                SEPARATE_ZVAL_IF_NOT_REF(array_ptr_ptr);
                if (opline->extended_value & ZEND_FE_FETCH_BYREF) {
                    Z_SET_ISREF_PP(array_ptr_ptr);
                }
            }
            array_ptr = *array_ptr_ptr;
            Z_ADDREF_P(array_ptr);
        }
    } else {
        ……
    }

    // 重置数组内部指针
    ……
}

从代码中可以看出,真正执行变量分离并不是在赋值语句执行的时候,而是推迟到了使用变量的时候,这也是Copy On Write机制在PHP中的实现。
FE_RESET之后,内存的变化如下:


上图解释了为何foreach并不会对原来的$arr产生影响。至于ref_count以及is_ref的变化情况,感兴趣的同学可以详细阅读ZEND_FE_RESET_SPEC_CV_HANDLER和ZEND_SWITCH_FREE_SPEC_VAR_HANDLER的具体实现(均位于php-src/zend/zend_vm_execute.h中),本文不做详细剖析:)

    
[3]浅析Apache中RewriteCond规则参数的详细介绍
    来源: 互联网  发布时间: 2013-11-30
RewriteCond就像我们程序中的if语句一样,表示如果符合某个或某几个条件则执行RewriteCond下面紧邻的RewriteRule语句,这就是RewriteCond最原始、基础的功能,为了方便理解,下面来看看几个例子。
代码如下:

RewriteEngine on
RewriteCond  %{HTTP_USER_AGENT}  ^Mozilla//5/.0.*
RewriteRule  index.php            index.m.php
RewriteCond  %{HTTP_USER_AGENT}  ^Lynx.*
RewriteRule  index.php            index.L.php
RewriteRule  index.php            index.b.php

上面语句的作用是当你是用FF浏览器访问index.php这个文件的时候,会自动让你访问到index.m.php这个文件,当你是用一些移动终端访问的 时候,会让你对index.php这个文件的访问实际访问的是index.L.php去,如果你是用其它的浏览器访问的时候,会让你跳到 index.b.php。在说形象一点,上面的语句就等同于程序里面的下面语句(依PHP语句为例):
代码如下:

if($_SERVER['HTTP_USER_AGENT'] == 'Mozilla/5.0')
{
//跳转到对index.m.php的访问
}
else if($_SERVER['HTTP_USER_AGENT'] == 'Lynx')
{
//跳转到对index.L.php的访问
}
else
//跳转到对index.b.php的访问

在看例2:
RewriteCond %{HTTP_REFERER} (www.test.cn)
RewriteRule (.*)$ test.php
上面语句的作用是如果你访问的上一个页面的主机地址是www.test.cn,则无论你当前访问的是哪个页面,都会跳转到对test.php的访问。
在看例三:
代码如下:

RewriteCond %{REMOTE_HOST} ^host1.* [OR]
RewriteCond %{REMOTE_HOST} ^host2.* [OR]
RewriteCond %{REMOTE_HOST} ^host3.*
RewriteRule (.*)$ test.php

上面语句的作用是如果你的地址是host1或host2或host3的时候,则就跳到对test.php。从这里可以看出,RewriteCond语句之间默认的是AND,如果想要OR,则要明确的写出来。
下面是自己收藏的一些有用的重写规则:
RewriteCond %{REQUEST_FILENAME} !-f   //如果文件存在,就直接访问文件,不进行下面的RewriteRule.(不是文件或文件不存在就执行重写)
RewriteCond %{REQUEST_FILENAME} !-d   //#如果目录存在就直接访问目录不进行RewriteRule
RewriteCond %{REQUEST_URI} !^.*(/.css|/.js|/.gif|/.png|/.jpg|/.jpeg)$ //#如果是这些后缀的文件,就直接访问文件,不进行Rewrite

    
最新技术文章:
▪PHP函数microtime()时间戳的定义与用法
▪PHP单一入口之apache配置内容
▪PHP数组排序方法总结(收藏)
▪php数组排序方法大全(脚本学堂整理奉献)
▪php数组排序的几个函数(附实例)
▪php二维数组排序(实例)
▪php根据键值对二维数组排序的小例子
▪php验证码(附截图)
▪php数组长度的获取方法(三个实例)
▪php获取数组长度的方法举例
▪判断php数组维度(php数组长度)的方法
▪php获取图片的exif信息的示例代码
▪PHP 数组key长度对性能的影响实例分析
▪php函数指定默认值的方法示例
▪php提交表单到当前页面、提交表单后页面重定...
▪php四舍五入的三种实现方法
▪php获得数组长度(元素个数)的方法
▪php日期函数的简单示例代码
▪php数学函数的简单示例代码
▪php字符串函数的简单示例代码
▪php文件下载代码(多浏览器兼容、支持中文文...
▪php实现文件下载、支持中文文件名的示例代码...
▪php文件下载(防止中文文件名乱码)的示例代码
▪解决PHP文件下载时中文文件名乱码的问题
▪php数组去重(一维、二维数组去重)的简单示例
▪php小数点后取两位的三种实现方法
▪php Redis 队列服务的简单示例
▪PHP导出excel时数字变为科学计数的解决方法
▪PHP数组根据值获取Key的简单示例
▪php数组去重的函数代码示例
 


站内导航:


特别声明:169IT网站部分信息来自互联网,如果侵犯您的权利,请及时告知,本站将立即删除!

©2012-2021,,E-mail:www_#163.com(请将#改为@)

浙ICP备11055608号-3