xue微xue微深入地聊一聊PHP session

  • 2019 年 11 月 25 日
  • 筆記

大家好,我今天打算换一个新的出场方式。所以,我打算从下面倒计时后开始重新打招呼,你们就假装开头这句话我没写配合一下,谢谢。

你们准备好了吗,我要重新从头开始了…

5

4

3

2

1

.

.

.

大家好!

事情是这样的,昨天晚上我先发了一篇关于API Token的文章,然后又引入了PHP Session,虽然这两篇文章阅读量创了历史新低(我仿佛看到了永强新欣慰的脸庞泛着笑容和淫光),但是还是依旧有些个问题就像屎一样甩在了我脸上,其中第一个问题角度还是比较刁钻的,你们感受下:

  • 老李,双11那么一大坨人访问PHP商城,PHP session id会不会重复啊? 答:你确定你们用户量过一千了吗?
  • 老李,为毛我多个控制器访问同一个session成员,其他页面会被卡住,你遇到过咩? 答:没遇到过,就特么你事儿多…告诉用户让TA们等等就行了,又不是不能用
  • 用什么方法可以精确控制PHP session过期以及删除 答:用爱

看到这三个令人绝望的回答,我穿过网线就已经听到了有人似乎在说:“ 老李,你变了… ”,然而我要告诉你并没有,你李哥办事你们心里不清楚么?


第一个问题

这个问题实际上是在考验session id的生成策略,抽象一下就是【某个空间中生成全局唯一的id】。这个其实没啥好说的,得去简单翻一下PHP源码中关于生成session id这里的部分了,我手里常年备着一份PHP 7.2.8的源码,但我基本没这么看过只是有需要的时候翻翻,比如现在。你可以在ext / session / session.c 文件里连蒙带搜加grep找到相关代码,你们感受下(如果我找错了,记得来打我脸,我专门出一期修正):

/* 这个叫做 php_session_create_id 的函数生成了session id 但是生成的核心函数是调用的 bin_to_readable 函数 */PHPAPI zend_string *php_session_create_id(PS_CREATE_SID_ARGS) /* {{{ */{  // 声明一个 char 数组,数组长度就是后面两个常量相加  unsigned char rbuf[PS_MAX_SID_LENGTH + PS_EXTRA_RAND_BYTES];  // zend_string 是zend封装好的字符串struct,类似于redis里d的 sds  // 这里是声明一个指向 zend_string 的指针  zend_string *outid;    /* Read additional PS_EXTRA_RAND_BYTES just in case CSPRNG is not safe enough */  // 这里看起来就是如果生成失败的情况.  if (php_random_bytes_throw(rbuf, PS(sid_length) + PS_EXTRA_RAND_BYTES) == FAILURE) {    return NULL;  }  // zend_string_alloc 应该是zend封装好的为string分配内存的函数  // 功能类似于 malloc 函数...  outid = zend_string_alloc(PS(sid_length), 0);  /*   ZSTR_LEN可以获取zend_string的长度   ZSTR_VAL可以获取zend_string的值   但谁能告诉我这个PS宏是做什么用的... ...   */  ZSTR_LEN(outid) = bin_to_readable(rbuf, PS(sid_length), ZSTR_VAL(outid), (char)PS(sid_bits_per_character));    return outid;}  static char hexconvtab[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,-";/* returns a pointer to the byte after the last valid character in out *//* * 注意这个函数里的玩意略风骚。至于你们能不能顶住,我反正顶不住 */static size_t bin_to_readable(unsigned char *in, size_t inlen, char *out, char nbits) /* {{{ */{  unsigned char *p, *q;  unsigned short w;  size_t len = inlen;  int mask;  int have;  p = (unsigned char *)in;  q = (unsigned char *)in + inlen;  w = 0;  have = 0;  mask = (1 << nbits) - 1;  while (inlen--) {    if (have < nbits) {      if (p < q) {        // 。。。。。。。。        // 你们有兴趣好好研究一下这行,反正我特么不看了,艹        w |= *p++ << have;        have += 8;      } else {         /* consumed everything? */         if (have == 0) break;         /* No? We need a final round */        have = nbits;      }    }    /* consume nbits */    // 关键行在这里...out是字符串数组指针    // 这里就是将hexconvtab数组里的字符一个一个    // 随机出来赋值给out指针    *out++ = hexconvtab[w & mask];    w >>= nbits;    have -= nbits;  }  // 这个,没啥好说的,就是给字符数组最后加上一个