`
syu
  • 浏览: 16032 次
  • 性别: Icon_minigender_1
  • 来自: 北京
最近访客 更多访客>>
文章分类
社区版块
存档分类
最新评论

PHP常见面试题目深入解答分析(一)

阅读更多

高手请无视本文,骨灰们请直接绕行... :-)

本文旨在对网上流传很广的部份初/中级PHP开发工程师面试题目做出一些解答.并对这些面试题对面试者的测试意义,以及题目本身做一些较为深入的分析.
也许大量的初/中级PHPer们在面试的时候已经发现,许多公司的面试题目其实基本类似.:-),或许出题者本身并不清楚这些题目究竟要考验应聘者哪方面的知识.(反正天下面试题一大抄).也有PHPer们认为这些面试题千篇一律,而且很没有意思,并不能看出应试者真实水平,时常抱怨:"单引号和双引号有什么区别,这种题目有什么意思吗?我只需要把东西做出来就OK了.",那么这些题目的背后究竟隐藏着什么样的细节呢?在这篇文章里,将做一些分析.

题目一: 双引号和单引号的区别在哪里?
按照官方手册的说法,"单引号字符串中出现的变量和转义序列不会被变量的值替代".按这样的描述.单引号和双引号的区别无非就在于 变量 和 转义序列两方面.

从 变量 方面来讲. 当用双引号或者定界符指定字符串时,其中的变量会被解析。比如以下例子

<?php

$v_char='test';

echo "$v_char";

?>

结果会输出 test

而如果是 echo '$v_char'; 时 ,会原样输出 $v_char

而转义符方面,如果是 echo "test\nnextline"; 则显示结果为

test

nextline

如果是单引号,则会原样显示 test\nnextline ,其中的\n并不会做为换行转义.

由此可以推导出的答案就是,一般来讲,单引号界定字符,要比双引号略微快.

如果更进一步研究到编译层面,在scanning阶段针对双引号的词法规则处理的确比单引号要多近一倍,

而在opcodes层面,我们来看一段代码:

<?php
$show_value = 123;
echo 'sing_quote'.$show_value;
echo "double_quote{$show_value}";

?>

将这段代码转换为opcodes后.结果如下:

1: <?php
2: $show_value = 123;
0 ASSIGN !0, 123
3: echo 'sing_quote'.$show_value;
1 CONCAT 'sing_quote', !0 =>RES[~1]
2 ECHO ~1
4: echo "double_quote{$show_value}";
3 ADD_STRING 'double_quote' =>RES[~2]
4 ADD_VAR ~2, !0 =>RES[~2]
5 ECHO ~2
6 RETURN 1

由此可以看出,双引号中直接显示变量也确实比单引号用(.)符号连接变量要略微复杂些.

所以总的来说,如果不需要使用转义字符,则使用单引号确实比双引号要来得划算.

题目二:传值与传引用有什么不同?(值传递与引用传递的区别)

官方手册中,对于引用部份有着如下描述:引用是符号表别名 PHP 的引用允许用两个变量来指向同一个内容

并且更进一步的指出:在 PHP 中,变量名和变量内容是不一样的,因此同样的内容可以有不同的名字。最接近的比喻是 Unix 的文件名和文件本身――变量名是目录条目,而变量内容则是文件本身。引用可以被看作是 Unix 文件系统中的 hardlink。

既然引用都指向着同样的内容,那么只要对其中任意一个变量名施以任何改变内容值的语句,那么访问其它指向这块内容的变量名时,将得到更新后的内容.比如如下代码:

$arr = array(1,2,3,4,5);

foreach($arr as &$v)

{

$v++;

}

print_r($arr);

将看到遍历数组后,数组中每一元素的值都被修改.

在函数(或方法)传参中,将一变量作为参数时,是以引用的方式传处,还是值的方式传入,主要表现形式是在sample_call_function($varname) 还是 sample_call_function(&$varname) 上.

从概念上分析,值传递是对内容进行"拷贝",引用传递是用内容进行"建立快捷方式",所以很多答题答案都讲值传递要复制一次内存数据,引用传递则省去了这个消耗,所以更划算.

但从PHP底层处理方式来看,这种结论回答略显草率了.PHP底层对于变量内存管理是基于常见的copy on write(写时复制) 和change on write(改变时复制).所以对于

$val = 'test';

debug_zval_dump($val);

在函数debug_zval_dump中使用到的$val,并不会多开辟一块内存空间来存放'test'的内容.

但对于

$val = 'test';

$a = &$val;

debug_zval_dump($val);
则又有不同,这时传参的$val则会新开辟一块内存空间来存放'test'. 这在PHP源码包,zend_execute.c中有完整的处理流程.

题目三:include和require的区别是什么?

官方手册中对此的描述是:这两种结构除了在如何处理失败之外完全一样。include() 产生一个警告而 require() 则导致一个致命错误。

这个解释其实已经相当详细了.当引用一个外部文件时,如果这个文件打开出错,require将直接中止程序,并抛出一个Fatal Error,而include只会抛出一个warning,并继续执行include下面的语句.

另外.我在一些网站上看到过对include和require区别做出的如下解释:

require ()函数工作方式与XSSI相类似;不管在程序的哪个部分使用了这个函数,只有程序一开始运行,头文件的内容就被作为程序本身的一部分来处理。因此,如果 您在一个条件判定语句中使用了require()函数,那么即使这个条件即使不为真,头文件也会被包含进来。

而include()函数只是在执行到这一条语句时才会把头文件内容包含进来。如果程序没运行到这里,那PHP是不会管它的。这就意味着,您在条件判定部分使用include时,它会完全按照您希望的那样工作。

那么这条解释的正确性究竟如何,我们可以做2个小实验:

文件名: 1.php

<?php

if(0) include('2.php');

if(0) require('2.php');

?>

假设我们只有1.php,没有2.php 按照上述说法,当条件不为真时,文件也被包含进来,那么因为2.php根本不存在,所以必然程序会报错.但是上述程序的执行结果显然否定了按上述说法进行的推理.

第二个实验,使用工具,查看上述代码最终生成的opcode:

1: <?php
2: if(0) include('2.php');
0 JMPZ 0, ->3
1 INCLUDE_OR_EVAL '2.php', INCLUDE
2 JMP ->3
3: if(0) require('2.php');
3 JMPZ 0, ->6
4 INCLUDE_OR_EVAL '2.php', REQUIRE
5 JMP ->6
6 RETURN 1

由opcode中,我们也很明显的看到了,include和require都是转换成 INCLUDE_OR_EVAL的OPCODE在执行,除了参数不同外,确实没有其它流程上的差别.

题目四:SESSION与COOKIE的相同点和不同点是什么?

SESSION和COOKIE均是用于将特定的数据保留下来,以便用于之后请求的方法.即:相同点是它们都基于为每个客户端在一个规定的访问时限里,保存下数据以供这个客户端之后请求使用.

其不同的地方在于访问方式和处理的机制不同:

COOKIE是基于特定的HTTP响应头,客户端收到相应指令后,将数据保存于客户端.之后每一次HTTP请求中,COOKIE数据都将被客户端送往服务器.服务器端脚本可通过 预定义变量数组 $_COOKIE来获取相关的值.
SESSION则是将数据保存于服务器端,但是和每一个客户端之间建立一个唯一的身份ID,即SESSION_ID,客户端向服务器发送这个SESSION_ID,服务器再根据SESSION_ID取出相应的数据.这个SESSION_ID的传递方式有两种,一是存放在客户端的cookie中,经由HTTP头隐式传递,要么经由URL显示传递.使用时可通过预定义变量数组 $_SESSION来获取相关的值.

将这两者比较而言,COOKIE数据由于存放在客户端,减轻了服务器端存储方面的压力.但数据的安全性相对来说就要差一些.所以如果依靠COOKIE存放用户登录信息等敏感数据时,一定要对其做加密处理.而SESSION数据存放在服务器端,客户端本身访问不到直接数据,安全性相对COOKIE来说稍稍好一些.但对服务器的IO操作及存储方面会带来一些压力.尤其是PHP默认的SESSION处理机制,实际上就是将SESSION数据串行化,然后存放到一个以SESSION_ID为名字的文本文件中,用户量多的情况下带来的开销不容忽视.好在PHP的SESSION机制是非常灵活的,提供了相应方法改变SESSION处理机制.比如放到memcached里或其它地方 :-) 这得另开文章单独讲了.

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics