PHP面试题总结

一.cookie与session的区别

1.cookie 是保存在“客户端”的,而session是保存在“服务端”的
2.session较安全,但访问量增多会比较占用你服务器的性能
3.所以个人建议:
将登陆信息等重要信息存放为SESSION
其他信息如果需要保留,可以放在COOKIE中

二.php如何防止SQL注入

使用预处理语句和参数化查询。预处理语句和参数分别发送到数据库服务器进行解析,参数将会被当作普通字符处理.两种选择来实现该方法:
1.PDO(默认情况使用PDO并没有让MySQL数据库执行真正的预处理语句)

$stmt = $pdo->prepare('SELECT * FROM employees WHERE name = :name');
 
$stmt->execute(array('name' => $name));
 
foreach ($stmt as $row) {
    // do something with $row
}

2.mysqli

$stmt = $dbConnection->prepare('SELECT * FROM employees WHERE name = ?');
$stmt->bind_param('s', $name);
 
$stmt->execute();
 
$result = $stmt->get_result();
while ($row = $result->fetch_assoc()) {
    // do something with $row
}

三.redis的好处

(1) 速度快,因为数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都是O(1)
(2) 支持丰富数据类型,支持string,list,set,sorted set,hash
(3) 支持事务,操作都是原子性,所谓的原子性就是对数据的更改要么全部执行,要么全部不执行
(4) 丰富的特性:可用于缓存,消息,按key设置过期时间,过期后将会自动删除

redis 最适合的场景

(1)、会话缓存(Session Cache)
(2)、全页缓存(FPC)
(3)、队列
(4),排行榜/计数器
(5)、发布/订阅

MySQL里有2000w数据,redis中只存20w的数据,如何保证redis中的数据都是热点数据

相关知识:redis 内存数据集大小上升到一定大小的时候,就会施行数据淘汰策略。redis 提供 6种数据淘汰策略:

volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰 
volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰 
volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰 
allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰 
allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰 
no-enviction(驱逐):禁止驱逐数据

四.禁用 COOKIE 后 SEESION 还能用吗?

可以,3种方法

  、    1. 设置php.ini配置文件中的“session.use_trans_sid = 1”,或者编译时打开了“--enable-trans-sid”选项,让PHP自动跨页传递Session ID。
       2. 手动通过URL传值、隐藏表单传递Session ID。
       3. 用文件、数据库等形式保存Session ID,在跨页过程中手动调用。

Session是用Session ID来确定当前对话所对应的服务器Session,而Session ID是通过Cookie来传递的,禁用Cookie相当于失去了Session ID,也就得不到Session了。

五.常用的Linux命令?

   ls          显示文件或目录
   -l           列出文件详细信息l(list)
   -a          列出当前目录下所有文件及目录,包括隐藏的a(all)
   mkdir         创建目录
   -p           创建目录,若无父目录,则创建p(parent)
   cd               切换目录
   touch          创建空文件
   echo            创建带有内容的文件。
   cat              查看文件内容
   cp                拷贝
   mv               移动或重命名
   rm               删除文件
   -r            递归删除,可删除子目录及文件
   -f            强制删除
   find              在文件系统中搜索某文件
   wc                统计文本中行数、字数、字符数
   grep             在文本文件中查找某个字符串
   rmdir           删除空目录
   tree             树形结构显示目录,需要安装tree包
   pwd              显示当前目录
   ln                  创建链接文件
   more、less  分页显示文本文件内容
   head、tail    显示文件头、尾内容
   ctrl+alt+F1  命令行全屏模式

2.Linux管道

将一个命令的标准输出作为另一个命令的标准输入。也就是把几个命令组合起来使用,后一个命令除以前一个命令的结果。
例:grep -r "close" /home/* | more       在home目录下所有文件中查找,包括close的文件,并分页输出。

3.vim使用

vim三种模式:命令模式、插入模式、编辑模式。使用ESC或i或:来切换模式。
命令模式下:
:q                      退出
:q!                     强制退出
:wq                   保存并退出
:set number     显示行号
:set nonumber  隐藏行号
/apache            在文档中查找apache 按n跳到下一个,shift+n上一个
yyp                   复制光标所在行,并粘贴
h(左移一个字符←)、j(下一行↓)、k(上一行↑)、l(右移一个字符→)

4.打包压缩相关命令

gzip:
bzip2:
tar:                打包压缩
     -c              归档文件
     -x              压缩文件
     -z              gzip压缩文件
     -j              bzip2压缩文件
     -v              显示压缩或解压缩过程 v(view)
     -f              使用档名
例:
tar -cvf /home/abc.tar /home/abc              只打包,不压缩
tar -zcvf /home/abc.tar.gz /home/abc        打包,并用gzip压缩
tar -jcvf /home/abc.tar.bz2 /home/abc      打包,并用bzip2压缩
当然,如果想解压缩,就直接替换上面的命令  tar -cvf  / tar -zcvf  / tar -jcvf 中的“c” 换成“x” 就可以了。

5.更改权限

sudo chmod [u所属用户  g所属组  o其他用户  a所有用户]  [+增加权限  -减少权限]  [r  w  x]   目录名 
例如:有一个文件filename,权限为“-rw-r----x” ,将权限值改为"-rwxrw-r-x",用数值表示为765
sudo chmod u+x g+w o+r  filename
上面的例子可以用数值表示
sudo chmod 765 filename

六. MySQL常用的存储引擎以及它们的区别

1.MyIASM
2.Innodb
构成上,MyISAM 的表在磁盘中有三个文件组成,分别是表定义文件( .frm)、数据文件(.MYD)、索引文件(.MYI),而 InnoDB 的表由表定义文件(.frm)、表空间数据和日志文件组成。

安全方面,MyISAM 强调的是性能,其查询效率较高,但不支持事务和外键等安全性方面的功能,而 InnoDB 支持事务和外键等高级功能,查询效率稍低。

对锁的支持,MyISAM 支持表锁,而 InnoDB 支持行锁。

优化MYSQL数据库的方法:

1.事务处理
2.使用join代替子查询
3.适当建立索引
4.数据库主从
5.分表
6.选取最适用的字段属性,尽可能减少定义字段长度,尽量把字段设置NOT NULL
7.使用联合(UNION)来代替手动创建的临时表

七.对于大流量的网站, 您采用什么样的方法来解决访问量问题

1.确认服务器硬件是否足够支持当前的流量
2.优化数据库访问
3.禁止外部的盗链
4.使用不同主机分流主要流量 [分布式]
5.缓存技术,是将动态数据存储到缓存文件中,动态网页直接调用这些文件,而不必再访问
6.控制大文件的下载 
7.优化前后端代码,不能有冗余代码
8.事务处理

八.如果某段与数据库交互的程序运行较慢你将如何处理

1.首先提高数据库的查询速度
2.优化程序代码
3.就是提高服务器的速度

九.如何解决Ajax跨越(站)问题

本文通过设置Access-Control-Allow-Origin来实现跨域。
1、允许单个域名访问
指定某域名(http://client.runoob.com)跨域访问,则只需在http://server.runoob.com/server.PHP文件头部添加

header('Access-Control-Allow-Origin:http://client.runoob.com');

2.允许多个域名访问
指定多个域名(http://client1.runoob.comhttp://client2.runoob.com等)跨域访问,则只需在http://server.runoob.com/server.php文件头部添加

$origin = isset($_SERVER['HTTP_ORIGIN'])? $_SERVER['HTTP_ORIGIN'] : '';  
  
$allow_origin = array(  
    'http://client1.runoob.com',  
    'http://client2.runoob.com'  
);  
  
if(in_array($origin, $allow_origin)){  
    header('Access-Control-Allow-Origin:'.$origin);       
} 

3.允许所有域名访问
允许所有域名访问则只需在http://server.runoob.com/server.php文件头部添加

header('Access-Control-Allow-Origin:*'); 

十.八百万条海量数据的数据库设计和网站处理方案

数据库设计

  如果不能设计一个合理的数据库模型,不仅会增加客户端和服务器段程序的编程和维护的难度,而且将会影响系统实际运行的性能。所以,在一个系统开始实施之前,完备的数据库模型的设计是必须的。

        在一个系统分析、设计阶段,因为数据量较小,负荷较低。我们往往只注意到功能的实现,而很难注意到性能的薄弱之处,等到系统投入实际运行一段时间后,才发现系统的性能在降低,这时再来考虑提高系统性能则要花费更多的人力物力,而整个系统也不可避免的形成了一个打补丁工程。

        所以在考虑整个系统的流程的时候,我们必须要考虑,在高并发大数据量的访问情况下,我们的系统会不会出现极端的情况。(例:对外统计系统在7月16日出现的数据异常的情况,并发大数据量的的访问造成,数据库的响应时间不能跟上数据刷新的速度造成。具体情况是:在日期临界时(00:00:00),判断数据库中是否有当前日期的记录,没有则插入一条当前日期的记录。在低并发访问的情况下,不会发生问题,但是当日期临界时的访问量相当大的时候,在做这一判断的时候,会出现多次条件成立,则数据库里会被插入多条当前日期的记录,从而造成数据错误),数据库的模型确定下来之后,我们有必要做一个系统内数据流向图,分析可能出现的瓶颈。

        为了保证数据库的一致性和完整性,在逻辑设计的时候往往会设计过多的表间关联,尽可能的降低数据的冗余。(例:用户表的地区,我们可以把地区另外存放到一个地区表中)如果数据冗余低,数据的完整性容易得到保证,提高了数据吞吐速度,保证了数据的完整性,清楚地表达数据元素之间的关系。而对于多表之间的关联查询(尤其是大数据表)时,其性能将会降低,同时也提高了客户端程序的编程难度,因此,物理设计需折衷考虑,根据业务规则,确定对关联表的数据量大小、数据项的访问频度,对此类数据表频繁的关联查询应适当提高数据冗余设计但增加了表间连接查询的操作,也使得程序的变得复杂,为了提高系统的响应时间,合理的数据冗余也是必要的。设计人员在设计阶段应根据系统操作的类型、频度加以均衡考虑。

         另外,最好不要用自增属性字段作为主键与子表关联。不便于系统的迁移和数据恢复。对外统计系统映射关系丢失。

       原来的表格必须可以通过由它分离出去的表格重新构建。使用这个规定的好处是,你可以确保不会在分离的表格中引入多余的列,所有你创建的表格结构都与它们的实际需要一样大。应用这条规定是一个好习惯,不过除非你要处理一个非常大型的数据,否则你将不需要用到它。(例如一个通行证系统,我可以将USERID,USERNAME,USERPASSWORD,单独出来作个表,再把USERID作为其他表的外键)。

表的设计具体注意的问题:

        1、数据行的长度不要超过8020字节,如果超过这个长度的话在物理页中这条数据会占用两行从而造成存储碎片,降低查询效率。

        2、能够用数字类型的字段尽量选择数字类型而不用字符串类型的(电话号码),这会降低查询和连接的性能,并会增加存储开销。这是因为引擎在处理查询和连接回逐个比较字符串中每一个字符,而对于数字型而言只需要比较一次就够了。

        3、对于不可变字符类型char和可变字符类型varchar 都是8000字节,char查询快,但是耗存储空间,varchar查询相对慢一些但是节省存储空间。在设计字段的时候可以灵活选择,例如用户名、密码等长度变化不大的字段可以选择CHAR,对于评论等长度变化大的字段可以选择VARCHAR。

        4、字段的长度在最大限度的满足可能的需要的前提下,应该尽可能的设得短一些,这样可以提高查询的效率,而且在建立索引的时候也可以减少资源的消耗。


海量数据的解决方案

使用缓存;
页面静态化技术;
数据库优化;
分离数据库中活跃的数据;
批量读取和延迟修改;
读写分离;
使用NoSQL和Hadoop等技术;
分布式部署数据库;
应用服务和数据服务分离;
使用搜索引擎搜索数据库中的数据;
进行业务的拆分;

高并发情况下的解决方案

应用程序和静态资源文件进行分离;
页面缓存;
集群与分布式;
反向代理;

十一.如何实现页面静态化缓存以及应用场景

php自带的ob缓存机制实现页面静态化的方法

ob_start():开启缓存机制
ob_get_contents():获取ob缓存中的内容
ob_clean()清除ob缓存中的内容,但不关闭缓存
ob_end_clean() 清除ob缓存中的内容,并关闭缓存
ob_flush 清空缓存,输出内容,但不关闭缓存
ob_end_flush 清空缓存,输出内容,并关闭缓存
flush强制刷新输出缓存中的内

应用场景

数据库中有些数据是完全静态的或不太经常变动的,缓存系统会通过把SQL查询的结果缓存到一个更快的存储系统中存储,从而避免频繁操作数据库而很大程度上提高了程序执行时间,而且缓存查询结果也允许你后期处理。

十二.你网上支付? 支付宝还是微信? return_url和notify_url有什么区别?

return_url:

(1) 买家在支付成功后会看到一个支付宝提示交易成功的页面,该页面会停留几秒,然后会自动跳转回商户指定的同步通知页面(参数return_url);
(2) 该页面中获得参数的方式,需要使用GET方式获取,如request.QueryString("out_trade_no")、$_GET['out_trade_no'];
(3) 该方式仅仅在买家付款完成以后进行自动跳转,因此只会进行一次;
(4) 该方式不是支付宝主动去调用商户页面,而是支付宝的程序利用页面自动跳转的函数,使用户的当前页面自动跳转;
(5) 基于(4)的原因,可在本机而不是只能在服务器上进行调试;
(6) 返回URL只有一分钟的有效期,超过一分钟该链接地址会失效,验证则会失败;
(7) 设置页面跳转同步通知页面(return_url)的路径时,不要在页面文件的后面再加上自定义参数。

notify_url

(1) 必须保证服务器异步通知页面(notify_url)上无任何字符,如空格、HTML标签、开发系统自带抛出的异常提示信息等;
(2) 支付宝是用POST方式发送通知信息,因此该页面中获取参数的方式,如:
request.Form("out_trade_no")、$_POST['out_trade_no']。
(3) 支付宝主动发起通知,该方式才会被启用;
(4) 只有在支付宝的交易管理中存在该笔交易,且发生了交易状态的改变,支付宝才会通过该方式发起服务器通知(即时到账中交易状态为“等待买家付款”的状态默认是不会发送通知的);
(5) 服务器间的交互,不像页面跳转同步通知可以在页面上显示出来,这种交互方式是不可见的;
(6) 第一次交易状态改变(即时到账中此时交易状态是交易完成)时,不仅页面跳转同步通知页面会启用,而且服务器异步通知页面也会收到支付宝发来的处理结果通知;
(7) 程序执行完后必须打印输出“success”(不包含引号)。如果商户反馈给支付宝的字符不是success这7个字符,支付宝服务器会不断重发通知,直到超过24小时22分钟。
一般情况下,25小时以内完成8次通知(通知的间隔频率一般是:2m,10m,10m,1h,2h,6h,15h);
(8) 程序执行完成后,该页面不能执行页面跳转。如果执行页面跳转,支付宝会收不到success字符,会被支付宝服务器判定为该页面程序运行出现异常,而重发处理结果通知;
(9) cookies、session等在此页面会失效,即无法获取这些数据;
(10) 该方式的调试与运行必须在服务器上,即互联网上能访问;
(11) 该方式的作用主要防止订单丢失,即页面跳转同步通知没有处理订单更新,它则去处理;
(12) 通知ID(参数notify_id)只有一分钟有效期,超过一分钟该次通知会验证失败。一旦验证成功下次再验证就会失效。

十三.如何设计无限极商品分类表

第一种:
id name parent_id
主键ID 商品名称 父分类

缺点: 查询时 需要通过递归计算商品分类的层级,效率低
优点: 添加和更新速度快

第二种:
id name parent_id level path
主键ID 商品名称 父分类 层级 层级路径

缺点:添加和修改时需要重新计算层级和路径,查询时需要通过path排序。
优点:添加和更新速度快。

第三种(嵌套集合):
id name parent_id lft rght
主键ID 商品名称 父分类 左边界 有边界

缺点:添加和更新复杂。
优点:将左右边界上添加索引后查询速度最快! 

十四.写一个验证邮箱的正则表达式?

$email = 'xxxx@itsource.cn';
$result = preg_match('\w+([-.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*', $email);
var_dump($result);  //0表示没有匹配到   非0表示匹配到。

十五.获取上个月最后一天?

//t 表示:给定月份所应有的天数 28 到 31
echo date('Y-m-t', strtotime('-1 month'));

十六.用 PHP 打印出前一天的时间格式是 2016-12-28 22:21:21?

//>>1.当前时间减去一天的时间,然后再格式化
echo date('Y-m-d H:i:s',time()-3600*24);

//>>2.使用strtotime,可以将任何字符串时间转换成时间戳,仅针对英文
echo date('Y-m-d H:i:s',strtotime('-1 day'));
//在实际开发的生活,strtotime非常有用,因为表单提交过来的数据都是字符串。
//比如: 2016-12-28,需要通过strtotime转换为时间戳保存到数据库中

十七.说出数组涉及到的常用函数

array --  声明一个数组
count -- 计算数组中的单元数目或对象中的属性个数
foreach -- 遍历数组
list -- 遍历数组
explode -- 将字符串转成数组
implode -- 将数组转成一个新字符串
array_merge -- 合并一个或多个数组
is_array -- 检查是否是数组
print_r -- 输出数组
sort -- 数组排序
array_keys -- 返回数组中所有的键名
array_values -- 返回数组中所有的值
key -- 从关联数组中取得键名

十八.单例模式(三私一公)

class DB{
    //私有化后在类内部保存对象并且防止外部访问到
    private static $obj=null;
    //私有化后防止在外部创建新的对象
    private function __construct() {
    }
    //公有并且静态方法在类外面可以通过类名直接访问
    public static function getInstance(){
        if(self::$obj==null)
            self::$obj=new self();
        return self::$obj;
    }
    //私有化克隆执行的方法,防止在外部被克隆
    private function __clone(){
    }
}

十九.5字符串的常用函数?

trim()  -- 去除字符串首尾处的空白字符(或者其他字符)
strlen()  -- 字符串长度
substr()  -- 截取字符串
str_replace()  -- 替换字符串函数
substr_replace()  -- 对指定字符串中的部分字符串进行替换
strstr()  -- 检索字符串函数
explode()  -- 分割字符串函数

二十.获取上传文件类型的几种方法?

第1种方法:
function get_extension($file)
{
substr(strrchr($file, '.'), 1);
}
第2种方法:
function get_extension($file)
{
$info = pathinfo($file);
return $info['extension'];
}
第3种方法:
function get_extension($file)
{
return pathinfo($file, PATHINFO_EXTENSION);
}
第4种方法:
//该方法可以防止伪装图片,判断该文件的真实后缀。 最安全。
function ftype($filename){
    $file   = fopen($filename, "rb");
    $bin    = fread($file, 2); //只读2字节
    fclose($file);
    $strInfo    = @unpack("C2chars", $bin);
    $typeCode   = intval($strInfo['chars1'].$strInfo['chars2']);
    $fileType   = '';
    switch($typeCode){
        case 7790:
            $fileType = 'exe';
            break;
        case 7784:
            $fileType = 'midi';
            break;
        case 8297:
            $fileType = 'rar';
            break;
        case 255216:
            $fileType = 'jpg';
            break;
        case 7173:
            $fileType = 'gif';
            break;
        case 6677:
            $fileType = 'bmp';
            break;
        case 13780:
            $fileType = 'png';
            break;
        default:
            $fileType = 'unknown';
    }
    return $fileType;
}

二十一.冒泡排序,快速排序

冒泡排序:

function bubbleSort($arr)
{
    $len=count($arr);
    //该层循环控制 需要冒泡的轮数
    for($i=1;$i<$len;$i++)
    { //该层循环用来控制每轮 冒出一个数 需要比较的次数
        for($k=0;$k<$len-$i;$k++)
        {
            if($arr[$k]>$arr[$k+1])
            {
  $tmp=$arr[$k+1];
                $arr[$k+1]=$arr[$k];
                $arr[$k]=$tmp;
            }
        }
    }
    return $arr;
}

快速排序:

function quick_sort($arr)
{
    //先判断是否需要继续进行
    $length = count($arr);
    if ($length <= 1) {
        return $arr;
    }
    //选择第一个元素作为基准
    $base_num = $arr[0];
    //遍历除了标尺外的所有元素,按照大小关系放入两个数组内
    //初始化两个数组
    $left_array = array();  //小于基准的
    $right_array = array();  //大于基准的
    for ($i = 1; $i < $length; $i++) {
        if ($base_num > $arr[$i]) {
            //放入左边数组
            $left_array[] = $arr[$i];
        } else {
            //放入右边
            $right_array[] = $arr[$i];
        }
    }
    //再分别对左边和右边的数组进行相同的排序处理方式递归调用这个函数
    $left_array = quick_sort($left_array);
    $right_array = quick_sort($right_array);
    //合并
    return array_merge($left_array, array($base_num), $right_array);;
}

$arr = array(5, 1, 0, 3, 9, 10, 59, 41, 78, 56, 45, 47, 12, 15, 45, 11);
$rs = quick_sort($arr);
print_r($rs);

推荐阅读更多精彩内容