PHP类型转换的面试题与答案解析
最近在为公司面试新人,经常会问到的一道题目就是PHP类型转换的值,例如:
var_dump((int)true);
var_dump((string)true);
var_dump((string)false);
var_dump((bool)"1");
var_dump((bool)"0");
var_dump((bool)"");
var_dump((bool)"false");
我印象中最早见到这道题目是在英极的PHP高级开发工程师岗位的笔试题里面,看似很基础,但是依然可以难住不少PHPer。先来看一下运行结果:
int(1)
string(1) "1"
string(0) ""
bool(true)
bool(false)
bool(false)
bool(true)
对于大多数人来说,第1、2、4行通常是没有问题的。但是为什么false转换为字符串是空字符串呢?在处理请求值时,通常会传一个字符串类型的false,但是“false”(字符串)并非false(布尔),这有点令人疑惑了。
为什么会这样呢?
关于这个问题,我们从PHP内核入手,看看在类型转换时系统内部到底发生了什么。
首先补充一些关于PHP弱类型实现方式的背景知识。PHP解释器是使用C语言写成的,当然最终对变量的处理,也会使用C语言构造数据结构来实现。在Zend引擎中,一个PHP变量对应的类型是zval。
打开Zend/zend_types.h文件,我们可以看到zval类型的定义,php-5.5.23版本大约在第55行左右:
typedef struct _zval_struct zval;
这样我们发现,zval其实是一个名为_zval_struct的结构体类型,我们在Zend/zend.h文件中找到这个结构体的定义,大约在320行左右开始:
typedef union _zvalue_value {
long lval; /* long value */
double dval; /* double value */
struct {
char *val;
int len;
} str;
HashTable *ht; /* hash table value */
zend_object_value obj;
} zvalue_value;
struct _zval_struct {
/* Variable information */
zvalue_value value; /* value */
zend_uint refcount__gc;
zend_uchar type; /* active type */
zend_uchar is_ref__gc;
};
大家可以看到,_zval_struct中包含两个重要的成员,一个是zvalue_value类型的value,一个是zend_uchar类型的type。注意zvalue_value类型是一个联合体,它用来存储一个PHP变量的值的信息。(如果你忘记了什么是联合体,我来解释一下。联合体类似结构体,但是联合体的中的成员,存储时有且只能有一个,而且联合体占用的空间是联合体中长度最长的那个成员,这样做是为了节省内存的使用。)在zvalue_value中,包括了long、double、struct、HashTable、zend_object_value五个类型的成员。他们分别用来存储PHP变量不同类型的值:
C类型 PHP类型
long bool
int
resource
double float
struct string
HashTable array
zend_object_value object
看到这个结构体之后,想必也就明白了常问的诸如PHP中int类型的取值范围,以及php中strlen的时间复杂度之类的问题。
由此可见,PHP的变量类型转换,或者说是弱类型实现,本质上是实现zval类型在不同类型之间的转换。除了完成zvalue_value的数值转换,还需要将_zval_struct中的type设置成当前变量的type类型。在Zend引擎中实现了convert_to_*系列函数完成这一转换,我们在Zend/zend_operators.c中可以看到这些转换函数,在大约511行左右,可以找到转换为布尔类型的函数:
ZEND_API void convert_to_boolean(zval *op) /* {{{ */
{
int tmp;
switch (Z_TYPE_P(op)) {
case IS_BOOL:
break;
case IS_NULL:
Z_LVAL_P(op) = 0;
break;
case IS_RESOURCE: {
TSRMLS_FETCH();
zend_list_delete(Z_LVAL_P(op));
}
/* break missing intentionally */
case IS_LONG:
Z_LVAL_P(op) = (Z_LVAL_P(op) ? 1 : 0);
break;
case IS_DOUBLE:
Z_LVAL_P(op) = (Z_DVAL_P(op) ? 1 : 0);
break;
case IS_STRING:
{
char *strval = Z_STRVAL_P(op);
if (Z_STRLEN_P(op) == 0
|| (Z_STRLEN_P(op)==1 && Z_STRVAL_P(op)[0]=='0')) {
Z_LVAL_P(op) = 0;
} else {
Z_LVAL_P(op) = 1;
}
STR_FREE(strval);
}
break;
case IS_ARRAY:
tmp = (zend_hash_num_elements(Z_ARRVAL_P(op))?1:0);
zval_dtor(op);
Z_LVAL_P(op) = tmp;
break;
case IS_OBJECT:
{
zend_bool retval = 1;
TSRMLS_FETCH();
convert_object_to_type(op, IS_BOOL, convert_to_boolean);
if (Z_TYPE_P(op) == IS_BOOL) {
return;
}
zval_dtor(op);
ZVAL_BOOL(op, retval);
break;
}
default:
zval_dtor(op);
Z_LVAL_P(op) = 0;
break;
}
Z_TYPE_P(op) = IS_BOOL;
}
/* }}} */
case IS_STRING这段代码即是将一个字符串类型变量转换为布尔型的操作。可以看到,只有空字符串,或者字符串长度为1,并且此字符为0时,字符串的布尔值才为1,也就是true,其他为0,也就是false。
同样的,我们也就明白了布尔值如何转换为字符串的,可以从_convert_to_string函数的实现中了解。
看似简单并且基础的PHP问题,究其根源是对PHP实现机制的把握。个人觉得,这道题也不失为鉴别PHPer知识边界的一道好题目。
cookie是一个存储在用户本地的一个小的文本文件了我们可以通过浏览器来访问它,下面我们来看看关于PHP 删除cookie的一些事项。
要删除 cookie 需要确保它的失效期是在过去,才能触发浏览器的删除机制
在php中,我们可以使用setcookie()函数来设置浏览器的Cookie信息。
常见的设置cookie的示例代码如下:
<?php
$name = 'mycookie'; // cookie名称
$value = 'CodePlayer'; // cookie值
$expire = time() + 3600 * 24 * 7; // 过期时间 7天
$path = '/'; // 设置可以使用该cookie的路径,'/'表示站点根目录,该目录及所有子目录中均可访问该cookie。
// 设置一个cookie
setcookie( $name, $value, $expire, $path );
?>
不过,我们想要删除Cookie信息的话,又该怎么办呢。php并没有提供另外一个专门用来删除Cookie的函数,而是直接使用setcookie()函数来删除Cookie信息,我们只需要将过期时间更改为当前时间之间的时间即可。
<?php
// 设置cookie已过期,浏览器即可删除该cookie。此时可以为任意值。
setcookie( 'mycookie', 'CodePlayer', time() - 3600, '/' );
// 或者
// 将过期时间直接设为0,表示1970-1-1(已经过期了),可以避免time()及数学运算的消耗
setcookie( 'mycookie', 'CodePlayer', 0, '/' );
?>
此外,我们还可以将Cookie的值设为空字符串(“”)或null,也可用来删除Cookie。
<?php
/* 删除cookie */
setcookie('mycookie', '');
// 或者
setcookie('mycookie', null);
?>
这样就完了吗?No!如果你直接如上使用空字符串或null的方式来删除Cookie,可能会导致对应的cookie无法删除。
当然,上述删除Cookie的方式是没有错的,错的是我们没有在删除Cookie时指定路径(第4个参数)。如果没有指定路径参数,则路径默认为当前请求URL所在目录。如果你设置Cookie时的路径与删除Cookie时的路径不一致,将无法删除该Cookie。
<?php
// 当前请求为:"/abc/cookie.php"
// 在路径"/"下设置cookie
setcookie( 'mycookie', 'CodePlayer', time() + 3600 * 24 * 7, '/' );
// 注意:这样删除是无效的,因为默认路径为当前目录,即:"/abc/"
// setcookie( 'mycookie', '');
// 删除路径"/"下设置的名为"mycookie"的cookie,此时时间值可随意,哪怕未过期也行
setcookie( 'mycookie', '', 0, '/');
?>
以下代码可以在php5.20的linux源码包中ext/standard/head.c第99行附近找到.
if (value && value_len == 0) {
/*
* MSIE doesn't delete a cookie when you set it to a null value
* so in order to force cookies to be deleted, even on MSIE, we
* pick an expiry date 1 year and 1 second in the past
*/
time_t t = time(NULL) - 31536001;
dt = php_format_date("D, d-M-Y H:i:s T", sizeof("D, d-M-Y H:i:s T")-1, t, 0 TSRMLS_CC);
sprintf(cookie, "Set-Cookie: %s=deleted; expires=%s", name, dt);
efree(dt);
} else {
sprintf(cookie, "Set-Cookie: %s=%s", name, value ? encoded_value : "");
if (expires > 0) {
strcat(cookie, "; expires=");
dt = php_format_date("D, d-M-Y H:i:s T", sizeof("D, d-M-Y H:i:s T")-1, expires, 0 TSRMLS_CC);
strcat(cookie, dt);
efree(dt);
}
}
源码中清清楚楚的显示,if (value && value_len == 0) ,当value_len为0
时
sprintf(cookie, "Set-Cookie: %s=deleted; expires=%s", name, dt);
会发送删除cookie的http头给浏览器.
最后我们可以得出结论,在php中使用
setcookie($cookiename, '');或者 setcookie($cookiename, NULL);
都会删除cookie,当然这些手册中并没有。
本文我们来分享一段可以同时ping多个ip然后对比找出网络最快的ip的php程序,这段程序用来找代理ip可是神器。
为了翻墙方便 ,买了个vpn,转到osx下面官方没有提供合适的客户端,无法选择最快的线路。于是就自己写了个脚本,手动ping出最快的IP.
代码如下 | 复制代码 |
$servers = array( array('VIP荷兰线路01','nl01.yyuu.me','81.4.105.195','4122或3108'), array('VIP法国线路01','fr01.yyuu.me','176.31.206.242','4122或3108'), array('VIP意大利线路01','it01.yyuu.me','37.247.48.226','4122或3108'), array('VIP新加坡线路01','sg01.yyuu.me','128.199.69.209','4122或3108') ); //配合pcntl_signal使用 declare(ticks=1); //最大的子进程数量 $max = count($servers); //当前的子进程数量 $child = 0; //当子进程退出时,会触发该函数 function sig_handler($sig) { switch($sig) { case SIGCHLD: //do something } } //注册子进程退出时调用的函数 pcntl_signal(SIGCHLD, "sig_handler"); foreach($servers as $server){ $pid = pcntl_fork(); if($pid){ //echo 'main thread start',PHP_EOL; }else{ exec('ping -c 1 '.$server[2],$result); echo $server[1].' '.$server[2].' '.$result[1],PHP_EOL; exit(); } } |
可以指定生成的字符串长度
function rand_str($length, $max=FALSE)
{
if (is_int($max) && $max > $length)
{
$length = mt_rand($length, $max);
}
$output = '';
for ($i=0; $i<$length; $i++)
{
$which = mt_rand(0,2);
if ($which === 0)
{
$output .= mt_rand(0,9);
}
elseif ($which === 1)
{
$output .= chr(mt_rand(65,90));
}
else
{
$output .= chr(mt_rand(97,122));
}
}
return $output;
}
调用实例:
$randstr = rand_str(16);
生成随机字符串的函数
<?php
/**
* 产生随机字符串
*
* 产生一个指定长度的随机字符串,并返回给用户
*
* @access public
* @param int $len 产生字符串的位数
* @return string
*/
function randStr($len=6) {
$chars='ABDEFGHJKLMNPQRSTVWXYabdefghijkmnpqrstvwxy23456789#%*'; // characters to build the password from
mt_srand((double)microtime()*1000000*getmypid()); // seed the random number generater (must be done)
$password='';
while(strlen($password)<$len)
$password.=substr($chars,(mt_rand()%strlen($chars)),1);
return $password;
}
?>
创建字符池。
function randomkeys($length)
{
$pattern = '1234567890abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLOMNOPQRSTUVWXYZ,./&l
t;>?;#:@~[]{}-_=+)(*&^%$?!'; //字符池
for($i=0; $i<$length; $i++)
{
$key .= $pattern{mt_rand(0,35)}; //生成php随机数
}
return $key;
}
echo randomkeys(8);
无需创建字符池
function randomkeys($length)
{
$output='';
for ($a = 0; $a < $length; $a++) {
$output .= chr(mt_rand(35, 126)); //生成php随机数
}
return $output;
}
echo randomkeys(8);
随机用户名和随机密码例子
//随机生成用户名(长度6-13)
function create_password($pw_length = 4){
$randpwd = '';
for ($i = 0; $i < $pw_length; $i++){
$randpwd .= chr(mt_rand(33, 126));
}
return $randpwd;
}
function generate_username( $length = 6 ) {
// 密码字符集,可任意添加你需要的字符
$chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()-_ []{}<>~`+=,.;:/?|';
$password = '';
for ( $i = 0; $i < $length; $i++ )
{
// 这里提供两种字符获取方式
// 第一种是使用substr 截取$chars中的任意一位字符;
// 第二种是取字符数组$chars 的任意元素
// $password .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
$password .= $chars[ mt_rand(0, strlen($chars) - 1) ];
}
return $password;
}
//调用
$userId = 'user'.generate_username(6);
$pwd = create_password(9);
mt_srand生成随机种子,密码的长度可以随意定义,最长32位。
<?php
mt_srand((double) microtime() * 1000000);
function gen_random_password($password_length = 32, $generated_password = ""){
$valid_characters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
$chars_length = strlen($valid_characters) - 1;
for($i = $password_length; $i--; ) {
//$generated_password .= $valid_characters[mt_rand(0, $chars_length)];
$generated_password .= substr($valid_characters, (mt_rand()%(strlen($valid_characters))), 1);
}
return $generated_password;
}
?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>php密码生成器</title>
<style type="text/css">
body {
font-family: Arial;
font-size: 10pt;
}
</style>
</head>
<body>
<span style="font-weight: bold; font-size: 15pt;">密码生成器</span><br /><br />
<?php
if (isset($_GET['password_length'])){
if(preg_match("/([0-9]{1,8})/", $_GET['password_length'])){
print("密码生成成功:<br />
<span style="font-weight: bold">" . gen_random_password($_GET['password_length']) . "</span><br /><br />n");
} else {
print("密码长度不正确!<br /><br />n");
}
}
print <<< end
请为密码生成其指定生成密码的长度:<br /><br />
<form action="{$_SERVER['PHP_SELF']}" method="get">
<input type="text" name="password_length">
<input type="submit" value="生成">
</form>
end;
?>
</body>
</html>
每一个web项目,在日PV量达到十万级或百万级抑或更高时,你的网站响应速度就决定你的项目是能够继续活下去,或立马死掉的关键
除了升级服务器配置,优化代码,优化数据库之外,另一个技术?页面静态化,也是你不得不采用的技术
我们知道,页面在输出之前,会将输出数据暂时存放到BUFFER(缓冲器)中,然后才会输出到页面;所以,BUFFER中的数据,
就是你PHP代码执行,并且模板渲染之后,所生成的静态数据,即html页面;对于数据更新不是很快,或者页面信息实时性要求
不是很高的时候,我们可以将BUFFER的数据写入到一个html文件中,下次访问时,直接请求这个html页面,这样,中间就免去了PHP代码执行时间,
数据库数据查询时间以及模板渲染的时间,对于数据库I/O量大的页面,能很大地提高页面的响应速度
具体实现如下:
*/ /** * 这里是你的PHP逻辑代码 */ //在你页面所有输出内容之前,开启缓冲器 ob_start(); echo ‘hello,word!’ //当使用ob_start()函数之后,页面不会有输出,此时,所有的输出都已经存放在缓存器中 //然后我们将缓存器里面的内容拿出来,存放到一个静态的Html文件中 file_put_contents(‘index.html’,ob_get_contents());//函数ob_get_contents()表示取出当前缓冲器内容; //然后关闭缓冲器并清除缓冲器内容 ob_end_clean(); /*这样,一个静态的html文件就生成了,然后,需要做的只是判断什么时候该生成html文件,什么时候直接访问Html文件 提示:通过filemtime()函数判断Index.html文件的上次修改时间和当前时间间隔,来控制缓存有效期: */ //设置换成有效期为120秒 $cacheTime = ‘120’; if(time()-filemtime(‘index.html’)>$cacheTime){ //不在缓存有效期,重新生成缓存文件 //这里是你的PHP逻辑代码和生成缓存文件的代码 }else{ //在缓存有效期内,直接调用index.html文件 }
相关文章
使用PHP+JavaScript将HTML页面转换为图片的实例分享
这篇文章主要介绍了使用PHP+JavaScript将HTML元素转换为图片的实例分享,文后结果的截图只能体现出替换的字体,也不能说将静态页面转为图片可以加快加载,只是这种做法比较interesting XD需要的朋友可以参考下...2016-04-19php中浮点型(float)和整型(integer)数据类型详解
文章分析了关于php中浮点型(float)和整型(integer)数据类型的用法区别以及在那种情况下会出现数据长度不够。 取值只能为True或者False,当其他类型转化为boolean类...2016-11-25- 在开发过程中,我们经常会将日期时间的毫秒数存放到数据库,但是它对应的时间看起来就十分不方便,我们可以使用一些函数将毫秒转换成date格式。 一、 在MySQL中,有内置的函数from_unixtime()来做相应的转换,使用如下: 复制...2014-05-31
- 本篇文章主要是对c#中数据类型占用的字节数进行了详细的介绍。需要的朋友可以过来参考下,希望对大家有所帮助...2020-06-25
- 这篇文章主要介绍了Javascript类型转换的规则实例解析,涉及到javascript类型转换相关知识,对本文感兴趣的朋友一起学习吧...2016-02-27
- 今天小编在这里就来给美图秀秀的这一款软件的使用者们来说下把普通照片快速转换成卡通效果的教程,各位想知道具体制作步骤的使用者们,那么下面就快阿里跟着小编一起看一...2016-09-14
- 在PHP中,大部分变量类型,如字符串,整型,浮点,数组等都是值类型的,而类和对象是引用类型,在使用的时候,需要注意这一点。看到网友在讨论PHP的&符号,要彻底理解它的用法,就有必要讨论一下变量的两种形式。PHP的变量在内存中是这样...2015-10-23
- 假如现在有这样一个表单,是添加元素用的。<form id='addForm' action='UserAdd.action' type='post'> <label for='uname'>用户名</label>:<input type='text' name='uname' id='uname'><br>...2015-11-24
- 虽然Javascript是弱类型语言,但是,它也有自己的几种数据类型,分别是:Number、String、Boolean、Object、Udefined、Null。其中,Object属于复杂数据类型,Object 由无序的键值对组成。其余几种都属于简单数据类型。注意:变量...2015-10-21
- 本篇文章是对C#中数据类型转换的几种形式进行了详细的分析介绍,需要的朋友参考下...2020-06-25
- 这篇文章主要介绍了C#泛型类型知识,文中代码非常详细,帮助大家更好的理解和学习,感兴趣的朋友可以了解下...2020-06-25
- 下面小编就为大家带来一篇java中String类型变量的赋值问题介绍。小编觉得挺不错的。现在分享给大家,给大家一个参考。...2016-03-28
- 这篇文章主要介绍了vue:el-input输入时限制输入的类型操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-08-05
- 这篇文章主要介绍了JavaScript实现Base64编码转换的相关资料,非常简单实用,需要的朋友可以参考下...2016-04-25
PHP编码转换函数mb_convert_encoding与iconv用法
文章来实现一个PHP编码转换函数mb_convert_encoding与iconv用法,希望例子能帮助到各位。 将一个短信接口代码从apache迁移到nginx+php-fpm后,发现无法发出短信了,查...2016-11-25- 这篇文章主要介绍了c#的类型转换详解,类型转换分两种形式:隐式转换、显示转换,下面是详细介绍...2020-06-25
- 这篇文章主要为大家详细介绍了JavaScript实现数据类型的相互转换,感兴趣的朋友可以参考一下...2016-03-09
- 本文主要介绍了C#的四种基本数据类型的相关知识,具有很好的参考价值。下面跟着小编一起来看下吧...2020-06-25
- 这篇文章主要给大家介绍了7道关于JS this的面试题,来看看你能答对几个,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-03-05
- 这篇文章主要介绍了C#将数字转换成字节数组的方法,涉及C#字符串操作的技巧,非常具有实用价值,需要的朋友可以参考下...2020-06-25