400-616-5551

您所在位置: 首页> 学习课程> PHP培训班 | PHP面试题整理-进阶篇

PHP培训班 | PHP面试题整理-进阶篇

发布百知教育 来源:学习课程 2020-02-26

1. yield是什么?举个使用场景

  • yield是生成器函数的核心关键字。

  • 使用场景:协程可以用在,异步网络 IO 的时候,使其成为非阻塞的。

使用示例:


  1. <?php


  2. header("content-type:text/html;charset=utf-8");


  3. function readTxt()


  4. {


  5.    # code...


  6.    $handle = fopen("./test.txt", 'rb');


  7.    while (feof($handle)===false) {


  8.        # code...


  9.        yield fgets($handle);


  10.    }


  11.    fclose($handle);


  12. }


  13. foreach (readTxt() as $key => $value) {


  14.    # code...


  15.    echo $value.'<br/>';


  16. }


使用生成器读取文件,第一次读取了第一行,第二次读取了第二行,以此类推,每次被加载到内存中的文字只有一行,大大的减小了内存的使用

在PHP中使用协程实现多任务调度

2.session共享方案

  • 搭建redis集群或者memcached集群,用集群自带的同步方法来帮我们在不同的主机中同步session,这样就相当于把原来的一份session变成了N分session,session的同步就依赖于NoSql集群的同步了。

  • 单独设置一个session服务器,负载服务器得到一个sessionid过后,去session服务器获得会话状态,然后根据状态来响应用户请求,如果会话状态为空,则在session服务器中设置一个会话状态,然后返回给用户一个sessionid。

3.php7.2为什么弃用__autoload

自动加载的原理,就是在我们new一个class的时候,PHP系统如果找不到你这个类,就会去自动调用本文件中的__autoload($class_name)方法,我们new的这个class_name 就成为这个方法的参数。所以我们就可以在这个方法中根据我们需要new class_name的各种判断和划分就去require对应的路径类文件,从而实现自动加载。

弃用原因:PHP不允许函数重名,所以一个项目中仅能出现一个__autoload函数。自己写的代码保证只有一个__autoload函数虽然有点难但也能做到,要是第三方库也定义了__autoload,那就很头疼了。__autoload的后继者是splautoload_register函数

4.PHP5和PHP7的Zval结构

PHP5 Zval结构如下:


  1. struct _zval_struct {


  2.     union {


  3.          long lval;


  4.          double dval;


  5.          struct {


  6.               char *val;


  7.               int len;


  8.          } str;


  9.          HashTable *ht;


  10.          zend_object_value obj;


  11.          zend_ast *ast;


  12.     } value;


  13.     zend_uint refcount__gc;


  14.     zend_uchar type;


  15.     zend_uchar is_ref__gc;


  16. };


PHP5 Zval结构存在的问题:

  • 整个结构体的大小是(在64位系统)24个字节, 而zval.value联合体中zend_object_value是最大的长板, 它导致整个value需要16个字节。

  • 结构体中的每一个字段都有明确的定义, 没有预留任何的自定义字段,在存储一些和zval相关的信息的时候,不得不采用其他方式来扩充zval

  • PHP的zval大部分都是按值传递,但有俩个例外(对象和资源),它们永远都是按引用传递,这样就造成一个问题, 对象和资源在除了zval中的引用计数以外, 还需要一个全局的引用计数, 这样才能保证内存可以回收。在PHP5的时代, 以对象为例, 它有俩套引用计数, 一个是zval中的, 另外一个是obj自身的计数。非此之外,需要多次读取内存, 才能获取到真正的objec对象本身

  • PHP5的时代调用MAKE_STD_ZVAL会在堆内存上分配一个临时变量

PHP7 Zval结构如下:


  1. struct _zval_struct {


  2.     union {


  3.          zend_long         lval;             /* long value */


  4.          double            dval;             /* double value */


  5.          zend_refcounted  *counted;


  6.          zend_string      *str;


  7.          zend_array       *arr;


  8.          zend_object      *obj;


  9.          zend_resource    *res;


  10.          zend_reference   *ref;


  11.          zend_ast_ref     *ast;


  12.          zval             *zv;


  13.          void             *ptr;


  14.          zend_class_entry *ce;


  15.          zend_function    *func;


  16.          struct {


  17.               uint32_t w1;


  18.               uint32_t w2;


  19.          } ww;


  20.     } value;


  21.    union {


  22.        struct {


  23.            ZEND_ENDIAN_LOHI_4(


  24.                zend_uchar    type,         /* active type */


  25.                zend_uchar    type_flags,


  26.                zend_uchar    const_flags,


  27.                zend_uchar    reserved)     /* call info for EX(This) */


  28.        } v;


  29.        uint32_t type_info;


  30.    } u1;


  31.    union {


  32.        uint32_t     var_flags;


  33.        uint32_t     next;                 /* hash collision chain */


  34.        uint32_t     cache_slot;           /* literal cache slot */


  35.        uint32_t     lineno;               /* line number (for ast nodes) */


  36.        uint32_t     num_args;             /* arguments number for EX(This) */


  37.        uint32_t     fe_pos;               /* foreach position */


  38.        uint32_t     fe_iter_idx;          /* foreach iterator index */


  39.    } u2;


  40. };


PHP7对Zval结构优化:

  • 整个结构体内部都是联合体,这个新的zval在64位环境下,现在只需要16个字节(2个指针size), 它主要分为俩个部分, value和扩充字段, 而扩充字段又分为u1和u2俩个部分, 其中u1是type info, u2是各种辅助字段.

  • 在PHP7中,移除了MAKE_STD_ZVAL/ALLOC_ZVAL宏, 不再支持存堆内存上申请zval. 函数内部使用的zval要么来自外面输入, 要么使用在栈上分配的临时zval.

  • 抽象的来说, PHP7中的zval, 已经变成了一个值指针, 要么保存着原始值, 要么保存着指向一个保存原始值的指针. 也就是说现在的zval相当于PHP5的时候的zval. 只不过相比于zval, 直接存储zval, 我们可以省掉一次指针解引用, 从而提高缓存友好性。

5.PHP内存管理机制与垃圾回收机制

PHP的内存管理机制:预先给出一块空间,用来存储变量,当空间不够时,再申请一块新的空间。

  • 存储变量名,存在符号表。

  • 变量值存储在内存空间。

  • 在删除变量的时候,会将变量值存储的空间释放,而变量名所在的符号表不会减小。

PHP垃圾回收机制

PHP5.2及之前版本:

PHP会根据引用计数refcount值来判断是不是垃圾,如果refcount值为0,PHP会当做垃圾释放掉,这种回收机制有缺陷,对于环状引用的变量无法回收。

PHP5.3及之后版本:

  • 如果发现一个 zval 容器中的 refcount 在增加,说明不是垃圾;

  • 如果发现一个 zval 容器中的 refcount 在减少,如果减到了0,直接当做垃圾回收;

  • 如果发现一个 zval 容器中的 refcount 在减少,并没有减到0,PHP 会把该值放到缓冲区,当做有可能是垃圾的怀疑对象;当缓冲区达到了临界值,PHP 会自动调用一个方法去遍历每一个值,如果发现是垃圾就清理。

6.Apache与Nginx的优缺点比较

1.nginx相对于apache的优点

  轻量级,比apache 占用更少的内存及资源。高度模块化的设计,编写模块相对简单抗并发,nginx处理请求是异步非阻塞,多个连接(万级别)可以对应一个进程,而apache 则是阻塞型的,是同步多进程模型,一个连接对应一个进程,在高并发下nginx 能保持低资源低消耗高性能。nginx处理静态文件好,Nginx 静态处理性能比 Apache 高 3倍以上

2.apache 相对于nginx 的优点: apache 的rewrite 比nginx 的rewrite 强大 ,模块非常多,基本想到的都可以找到 ,比较稳定,少bug ,nginx的bug相对较多

3.Nginx比Apache快的原因

  这得益于Nginx使用了最新的epoll(Linux 2.6内核)和kqueue(freebsd)网络I/O模型,而Apache则使用的是传统的select模型。

目前Linux下能够承受高并发访问的 Squid、Memcached都采用的是epoll网络I/O模型。处理大量的连接的读写,Apache所采用的select网络I/O模型非常低效。

7.fastcgi、cgi、php-fpm

fastcgi和cgi的区别

在web服务器方面在对数据进行处理的进程方面:

  • cgi fork一个新的进程进行处理读取参数,处理数据,然后就结束生命期。

  • fastcgi 用tcp方式跟远程机子上的进程或本地进程建立连接要开启tcp端口,进入循环,等待数据的到来,处理数据。

php-fpm的作用

那PHP-FPM又是什么呢?它是一个实现了Fastcgi协议的程序,用来管理Fastcgi起的进程的,即能够调度php-cgi进程的程序。现已在PHP内核中就集成了PHP-FPM,使用--enalbe-fpm这个编译参数即可。另外,修改了php.ini配置文件后,没办法平滑重启,需要重启php-fpm才可。此时新fork的worker会用新的配置,已经存在的worker继续处理完手上的活

举个例子: 服务端现在有个10万个字单词, 客户每次会发来一个字符串,问以这个字符串为前缀的单词有多少个。那么可以写一个程序,这个程序会建一棵trie树,然后每次用户请求过来时可以直接到这个trie去查找。但是如果以cgi的方式的话,这次请求结束后这课trie也就没了,等下次再启动该进程时,又要新建一棵trie树,这样的效率就太低下了。而用fastcgi的方式的话,这课trie树在进程启动时建立,以后就可以直接在trie树上查询指定的前缀了

8.HTTP Keep-Alive的作用

作用: Keep-Alive使客户端到服务器端的连接持续有效,当出现对服务器的后继请求时,Keep-Alive功能避免了建立或者重新建立连接。Web服务器,基本上都支持HTTP Keep-Alive。

缺点: 对于提供静态内容的网站来说,这个功能通常很有用。但是,对于负担较重的网站来说,虽然为客户保留打开的连 接有一定的好处,但它同样影响了性能,因为在处理暂停期间,本来可以释放的资源仍旧被占用。当Web服务器和应用服务器在同一台机器上运行时,Keep- Alive功能对资源利用的影响尤其突出。

解决:


  1. Keep-Alive: timeout=5, max=100


  2. # timeout:过期时间5秒(对应httpd.conf里的参数是:KeepAliveTimeout)


  3. # max是最多一百次请求,强制断掉连接。


  4. # 就是在timeout时间内又有新的连接过来,同时max会自动减1,直到为0,强制断掉。


9.写个函数来解决多线程同时写一个文件的问题。


  1. function write(){


  2.    //打开文件


  3.    $file = fopen('flock.text','w+');


  4.    if(!$file){


  5.      return 'the file not exist!';


  6.    }


  7.    //获取锁


  8.    if (flock(file,LOCK_EX)){


  9.       //todo


  10.       fwrite(file,'do some things');


  11.       //释放锁


  12.       flock(file,LOCK_UN);


  13.    } else {


  14.      return 'the file is write...';


  15.    }


  16.    //关闭文件


  17.    fclose(file);


  18. }


10.写一个函数,算出两个文件的相对路径


  1. function countOppose(){


  2.   $arr1 = explode('/', $pathA);


  3.   $arr2 = explode('/', $pathB);


  4.   // 获取相同路径的部分


  5.   $intersection = array_intersect_assoc($arr1, $arr2);


  6.   $depth =count($intersection);


  7.   // 将path2的/ 转为 ../,path1获取后面的部分,然后合拼


  8.   // 计算前缀


  9.   if (count($arr2) - $depth - 1 > 0) {


  10.       $prefix = array_fill(0, count($arr2) - $depth - 1, '..');


  11.   } else {


  12.       $prefix = array('.');


  13.   }


  14.   $tmp = array_merge($prefix, array_slice($arr1, $depth));


  15.   $relativePath = implode('/', $tmp);


  16.   return $relativePath;


  17. }


11.遍历一个文件夹下的所有文件和子文件夹


  1. function childForDir($dir)


  2. {


  3.    $files = [];


  4.    if (!is_dir($dir)) {


  5.        return $dir;


  6.    }


  7.    $handle = opendir($dir);


  8.    if (!$handle) {


  9.        return false;


  10.    }


  11.    //取出.和..


  12.    readdir($handle);


  13.    readdir($handle);


  14.   //遍历剩余的文件和目录


  15.    while ($file = readdir($handle)) {


  16.        if (is_dir($file)) {


  17.            $files[$file] = $this->childForDir($file);


  18.        } else {


  19.            $files[] = $dir . '/' . $file;


  20.        }


  21.    }


  22.    closedir($handle);


  23.    return $files;


  24. }


11.实现一个二分查找函数

二分查找又称折半查找,优点是比较次数少,查找速度快,平均性能好;其缺点是要求待查表为有序表,且插入删除困难。因此,折半查找方法适用于不经常变动而查找频繁的有序列表。


  1. <?php


  2. /**


  3. * @param $data //待查找的元素数组


  4. * @param $min //开始元素的下标


  5. * @param $max //结束元素的下标


  6. * @param $k //待查找的元素


  7. * @return bool


  8. */


  9. function binarySearch($data,$min,$max,$k){


  10.    if ($min <= $max){


  11.        //计算中间的元素下标


  12.        $mid = intval(($min +$max)/2);


  13.        if ($data[$mid] == $k){


  14.            //如果相等,则找到


  15.            return $mid;


  16.        } else if ($k < $data[$mid]){


  17.            //元素下标在前面一部分


  18.            return binarySearch($data, $min, $mid-1, $k);


  19.        } else {


  20.            //元素下标在后面一部分


  21.            return binarySearch($data, $mid+1, $max, $k);


  22.        }


  23.    }


  24.    return false;


  25. }


12.echo(int)((0.1+0.7) * 10); 输出是多少?为什么?

输出的结果为:7

关于浮点数精度的警告: 显然简单的十进制分数如同 0.1 或 0.7 不能在不丢失一点点精度的情况下转换为内部二进制的格式,这就会造成混乱的结果。例如,floor((0.1+0.7)*10) 通常会返回 7 而不是预期中的 8,因为该结果内部的表示其实是类似 7.9。

注意:永远不要相信浮点数结果精确到了最后一位,也永远不要比较两个浮点数是否相等

13.什么是写时复制

写时复制(Copy on Write,也缩写为COW)的应用场景非常多, 比如Linux中对进程复制中内存使用的优化,在各种编程语言中,如C++的STL等等中均有类似的应用。COW是常用的优化手段,可以归类于:资源延迟分配。只有在真正需要使用资源时才占用资源, 写时复制通常能减少资源的占用。

在开始之前,我们可以先看一段简单的代码:


  1. <?php   //例一


  2.    $foo = 1;


  3.    $bar = $foo;


  4.    echo $foo + $bar;


  5. ?>


执行这段代码,会打印出数字2。从内存的角度来分析一下这段代码"可能”是这样执行的:分配一块内存给foo变量,里面存储一个1;再分配一块内存给bar变量,也存一个1,最后计算出结果输出。事实上,我们发现foo和bar变量因为值相同,完全可以使用同一块内存,这样,内存的使用就节省了一个1, 并且,还省去了分配内存和管理内存地址的计算开销。没错,很多涉及到内存管理的系统,都实现了这种相同值共享内存的策略:写时复制



PHP培训班:http://www.baizhiedu.com/php2019

 

上一篇:PHP培训班 | 面试题整理-PHP基础篇

下一篇:提高Python数据分析速度的八个小技巧

相关推荐

关闭

立即申请

www.baizhiedu.com