php源码之strip_tags

时间:2022-08-27 17:08:08

php strip_tags实现的c源码
函数声明:/php5/ext/stardard/php_string.h
函数原型:/php5/ext/stardard/string.c

/* {{{ proto string strip_tags(string str [, string allowable_tags])
   Strips HTML and PHP tags from a string */
PHP_FUNCTION(strip_tags)
{
char *buf;
zval **str, **allow=NULL;
char *allowed_tags=NULL;
int allowed_tags_len=0;
size_t retval_len;

switch (ZEND_NUM_ARGS()) {//参数判断
   case 1:
    if (zend_get_parameters_ex(1, &str) == FAILURE) {//若参数只有一个并且参数未取到,返回false
     RETURN_FALSE;
    }
    break;
   case 2:
    if (zend_get_parameters_ex(2, &str, &allow) == FAILURE){//若参数有二个并且参数未取到,返回false
     RETURN_FALSE;
    }
    convert_to_string_ex(allow); //将允许不被过滤的标签强转为字符串,
    allowed_tags = Z_STRVAL_PP(allow); //得到allow的内容
    allowed_tags_len = Z_STRLEN_PP(allow);//得到allow的长度 
    break;
   default:
    WRONG_PARAM_COUNT;
    break;
}
convert_to_string_ex(str);
buf = estrndup(Z_STRVAL_PP(str), Z_STRLEN_PP(str)); //就是拷贝了一个字符串
/***************************************************
estrndup() 用于替代strndup()。速度要快于 estrdup() 而且是二进制安全的。如果你在复制之前预先知道这个字符串的长度那就推荐你使用这个函数。 
Zend\zend_alloc.h(61): #define estrndup(s, length) _estrndup((s), (length) ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC)

ZEND_API char *_estrndup(const char *s, uint length ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)
{
   char *p;

   p = (char *) _emalloc(length+1 ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); 
   //封装了c的malloc函数,其功能就是申请一段内存,return malloc(size),函数原型 在zend_alloc.c的1859行
  
   if (!p) {
    return (char *)NULL;
   }
   memcpy(p, s, length);//完全拷贝s的前length长度的内容,和strcpy不同的是遇到\0也不停止
   p[length] = 0;将字符串的最后一个字符设置为结束符 0和'\0'一样,若字符串最后一个字符不是这个的话,再对这个字串进行操作的时候会导致内存溢出
   return p;
}
***************************************************/

retval_len = php_strip_tags_ex(buf, Z_STRLEN_PP(str), NULL, allowed_tags, allowed_tags_len, 0);

/****************************************************
函数原型string.c 4130行--4345行   可结合字符串"abc<a href="";看他的算法
基本流程是一个字符一个字符的去判断,遇到<改变状态为1,2,3等,以后字符不计入,遇到>后回归状态为0,字符重新开始计入返回串
PHPAPI size_t php_strip_tags_ex(char *rbuf, int len, int *stateptr, char *allow, int allow_len, zend_bool allow_tag_spaces)
{ //size_t是一种无符号的整型数,它的取值没有负数,在数组中也用不到负数,而它的取值范围是整型数的双倍。sizeof操作符的结果类型是size_t,它在头文件中typedef为unsigned int类型。该类型保证能容纳实现所建立的最大对象的字节大小。
   char *tbuf, *buf, *p, *tp, *rp, c, lc;
   int br, i=0, depth=0;
   int state = 0;//0表示正常字符,1表示html代码,2表示php代码 4表示js,css等

   if (stateptr)
    state = *stateptr;//预定义去哪种标签

   buf = estrndup(rbuf, len);//将rbuf(传进来的字符串)复制一份
   c = *buf; //c得到buf的起始地址,第一个字符
   lc = '\0';//lc是最后一个字符
   p = buf;//整个字符串,用来做循环用
   rp = rbuf;//初始为整个字符串,用来存放过滤后的字串
   br = 0;//括号的级数
   if (allow) {
    php_strtolower(allow, allow_len);//如果存在允许不被过滤的标签,全部转为小些
    tbuf = emalloc(PHP_TAG_BUF_SIZE+1);//申请一k内存空间,用来存放允许过滤的字符串 PHP_TAG_BUF_SIZE为1023
    tp = tbuf;
   } else {
    tbuf = tp = NULL;
   }

   while (i < len) {
    switch (c) {
     case '\0'://若到结尾了,跳出
      break;
     case '<'://遇到<的字符 ,若下个字符是空格就当成正常字符跳到 正常处理的case 若不是,则将该字符计入到tp(存放<....>字符串的变量)中
      if (isspace(*(p + 1)) && !allow_tag_spaces) {
       goto reg_char;
      }
      if (state == 0) {
       lc = '<';
       state = 1;
       if (allow) {
        tp = ((tp-tbuf) >= PHP_TAG_BUF_SIZE ? tbuf: tp);//若马上要超出内存了,将指针再指向开头
        *(tp++) = '<';
       }
      } else if (state == 1) {
       depth++;
      }
      break;

     case '('://遇到( 判断是否正在过滤代码,若不是,正常字符处理,如是,则计入到tp中
      if (state == 2) {
       if (lc != '"' && lc != '\'') {
        lc = '(';
        br++;
       }
      } else if (allow && state == 1) {
       tp = ((tp-tbuf) >= PHP_TAG_BUF_SIZE ? tbuf: tp);
       *(tp++) = c;
      } else if (state == 0) {
       *(rp++) = c;
      }
      break;

     case ')'://遇到( 判断是否正在过滤代码,若不是,正常字符处理,如是,则计入到tp中
      if (state == 2) {
       if (lc != '"' && lc != '\'') {
        lc = ')';
        br--;
       }
      } else if (allow && state == 1) {
       tp = ((tp-tbuf) >= PHP_TAG_BUF_SIZE ? tbuf: tp);
       *(tp++) = c;
      } else if (state == 0) {
       *(rp++) = c;
      }
      break;

     case '>':
      if (depth) {//若是 <<>> 这样的情况下,直接跳出
       depth--;
       break;
      }
    
      switch (state) {
       case 1: /* HTML/XML */
        lc = '>';
        state = 0;
        if (allow) {
         tp = ((tp-tbuf) >= PHP_TAG_BUF_SIZE ? tbuf: tp);
         *(tp++) = '>';
         *tp='\0';
         if (php_tag_find(tbuf, tp-tbuf, allow)) {//查找下<>标签中的是否是存在允许的不过滤的标签中
          memcpy(rp, tbuf, tp-tbuf);//若果存在将这些标签加到返回字符串后面,即把tbuf前tp-tbuf个字符复制到rp指向的内存地址上
          rp += tp-tbuf;//把指针指向 目前所在的位置
         }
         tp = tbuf;//再次回归
        }
        break;
       
       case 2: /* PHP */
        if (!br && lc != '\"' && *(p-1) == '?') {
         state = 0;
         tp = tbuf;
        }
        break;
       
       case 3:
        state = 0;
        tp = tbuf;
        break;

       case 4: /* JavaScript/CSS/etc... */
        if (p >= buf + 2 && *(p-1) == '-' && *(p-2) == '-') {
         state = 0;
         tp = tbuf;
        }
        break;

       default:
        *(rp++) = c;
        break;
      }
      break;

     case '"':
     case '\'':
      if (state == 2 && *(p-1) != '\\') {//如果是要过滤php代码 并且 前一个字符是 \ 将最后一个字符lc设置为 当前字符或者 \0
       if (lc == c) {
        lc = '\0';
       } else if (lc != '\\') {
        lc = c;
       }
      } else if (state == 0) {//如果是正常字符,正常处理
       *(rp++) = c;
      } else if (allow && state == 1) {//如果是html代码中的,则将该字符添加到 允许不被过滤的字符串中去
       tp = ((tp-tbuf) >= PHP_TAG_BUF_SIZE ? tbuf: tp);
       *(tp++) = c;
      }
      break;
    
     case '!': 
      /* JavaScript & Other HTML scripting languages */
      if (state == 1 && *(p-1) == '<') { 
       state = 3;
       lc = c;
      } else {
       if (state == 0) {
        *(rp++) = c;
       } else if (allow && state == 1) {
        tp = ((tp-tbuf) >= PHP_TAG_BUF_SIZE ? tbuf: tp);
        *(tp++) = c;
       }
      }
      break;

     case '-': //如果钱几个字符是!--则将状态3置为4,否则以正常字符算
      if (state == 3 && p >= buf + 2 && *(p-1) == '-' && *(p-2) == '!') {
       state = 4;
      } else {
       goto reg_char;
      }
      break;

     case '?'://如果前个字符是<并state=1,则将状态变为2即过滤php代码

      if (state == 1 && *(p-1) == '<') { 
       br=0;
       state=2;
       break;
      }

     case 'E':
     case 'e'://如果是doctype 则将状态置为1,即过滤html
      /* !DOCTYPE exception */
      if (state==3 && p > buf+6
            && tolower(*(p-1)) == 'p'
               && tolower(*(p-2)) == 'y'
            && tolower(*(p-3)) == 't'
            && tolower(*(p-4)) == 'c'
            && tolower(*(p-5)) == 'o'
            && tolower(*(p-6)) == 'd') {
       state = 1;
       break;
      }
      /* fall-through */

     case 'l'://如果状态是过滤php,且是前几个字符是xml,则将状态置为过滤html

      /* swm: If we encounter '<?xml' then we shouldn't be in
      * state == 2 (PHP). Switch back to HTML.
      */

      if (state == 2 && p > buf+2 && *(p-1) == 'm' && *(p-2) == 'x') {
       state = 1;
       break;
      }

      /* fall-through */
     default:
reg_char:
      if (state == 0) {
       *(rp++) = c;
      } else if (allow && state == 1) {
       tp = ((tp-tbuf) >= PHP_TAG_BUF_SIZE ? tbuf: tp);
       *(tp++) = c;
      } 
      break;
    }
    c = *(++p);
    i++;
   } 
   if (rp < rbuf + len) {
    *rp = '\0';
   }
   efree(buf);//封装内存释放函数void free(void *ptr);的函数
   if (allow)
    efree(tbuf);
   if (stateptr)
    *stateptr = state;

   return (size_t)(rp - rbuf);//返回过滤后的长度
}
****************************************************/

RETURN_STRINGL(buf, retval_len, 0);//返回retval_len长度的字符串
}