PHP面试题总结
php常用数组函数
数组的基本操作
数组的键名和值
- array_values($arr); 获得数组的值
- array_keys($arr); 获得数组的键名
- array_flip($arr); 数组中的值与键名互换(如果有重复前面的会被后面的覆盖)
- in_array("apple",$arr); 在数组中检索apple
- array_search("apple",$arr); 在数组中检索apple ,如果存在返回键名
- array_key_exists("apple",$arr); 检索给定的键名是否存在数组中
- isset($arr[apple]): 检索给定的键名是否存在数组中
数组的内部指针
- current($arr); 返回数组中的当前单元
- pos($arr); 返回数组中的当前单元
- key($arr); 返回数组中当前单元的键名
- prev($arr); 将数组中的内部指针倒回一位
- next($arr); 将数组中的内部指针向前移动一位
- end($arr); 将数组中的内部指针指向最后一个单元
- reset($arr; 将数组中的内部指针指向第一个单元
- each($arr); 将返回数组当前元素的一个键名/值的构造数组,并使数组指针向前移动一位
- list($key,$value)=each($arr); 获得数组当前元素的键名和值
数组和变量之间的转换
- extract($arr);用于把数组中的元素转换成变量导入到当前文件中,键名当作变量名,值作为变量值。注:(第二个参数很重要,可以看手册使用)使用方法 echo $a;
- compact(var1,var2,var3);用给定的变量名创建一个数组
数组的分段和填充
数组分段
- array_slice($arr,0,3); 可以将数组中的一段取出,此函数忽略键名
- array_splice($arr,0,3,array("black","maroon")); 可以将数组中的一段取出,与上个函数不同在于返回的序列从原数组中删除
分割多个数组
- array_chunk($arr,3,TRUE); 可以将一个数组分割成多个,TRUE为保留原数组的键名数组的填充
- array_pad($arr,5,'x'); 将一个数组填补到指定长度
数组与栈
- array_push($arr,"apple","pear"); 将一个或多个元素压入数组栈的末尾(入栈),返回入栈元素的个数
- array_pop($arr); 将数组栈的最后一个元素弹出(出栈)
数组与列队
- array_shift($arr);数组中的第一个元素移出并作为结果返回(数组长度减1,其他元素向前移动一位,数字键名改为从零开始,文字键名不变)
- array_unshift($arr,"a",array(1,2));在数组的开头插入一个或多个元素
回调函数
- array_walk($arr,'function','words'); 使用用户函数对数组中的每个成员进行处理(第三个参数传递给回调函数function)
- array_walk_recursive($arr, 'function', 'words'); 对数组中的每个元素应用用户自定义函数。在函数中,数组的键名和键值是参数。该函数与 array_walk() 函数的不同在于可以操作更深的数组(一个数组中包含另一个数组)。
- array_map("function",$arr1,$arr2); 可以处理多个数组(当使用两个或更多数组时,他们的长度应该相同)
- array_filter($arr,"function"); 使用回调函数过滤数组中的每个元素,如果回调函数为TRUE,数组的当前元素会被包含在返回的结果数组中,数组的键名保留不变
- array_reduce($arr,"function",""); 转化为单值函数(为数组的第一个值)
数组的排序
通过元素值对数组排序
- sort($arr); 由小到大的顺序排序(第二个参数为按什么方式排序)忽略键名的数组排序
- rsort($arr); 由大到小的顺序排序(第二个参数为按什么方式排序)忽略键名的数组排序
- usort($arr,"function"); 使用用户自定义的比较函数对数组中的值进行排序(function中有两个参数,0表示相等,正数表示第一个大于第二个,负数表示第一个小于第二个)忽略键名的数组排序
- asort($arr); 由小到大的顺序排序(第二个参数为按什么方式排序)保留键名的数组排序
- arsort($arr); 由大到小的顺序排序(第二个参数为按什么方式排序)保留键名的数组排序
- uasort($arr,"function"); 使用用户自定义的比较函数对数组中的值进行排序(function中有两个参数,0表示相等,正数表示第一个大于第二个,负数表示第一个小于第二个)保留键名的数组排序
通过键名对数组排序
- ksort($arr); 按照键名正序排序
- krsort($arr); 按照键名逆序排序
- uksort($arr,"function"); 使用用户自定义的比较函数对数组中的键名进行排序(function中有两个参数,0表示相等,正数表示第一个大于第二个,负数表示第一个小于第二个)
自然排序法排序
- natsort($arr); 自然排序(忽略键名)
- natcasesort($arr); 自然排序(忽略大小写,忽略键名)
数组的计算
- array_sum($arr); 对数组内部的所有元素做求和运算
- array_merge($arr1,$arr2); 合并两个或多个数组(相同的字符串键名,后面的覆盖前面的,相同的数字键名,后面的不会做覆盖操作,而是附加到后面)
- “+”$arr1+$arr2; 对于相同的键名只保留第一个(详细说明可参考:PHP数组合并)
- array_merge_recursive($arr1,$arr2); 递归合并操作,如果数组中有相同的字符串键名,这些值将被合并到一个数组中去。如果一个值本身是一个数组,将按照相应的键名把它合并为另一个数组。当数组 具有相同的数组键名时,后一个值将不会覆盖原来的值,而是附加到后面
- array_diff($arr1,$arr2); 返回差集结果数组
- array_diff_assoc($arr1,$arr2,$arr3); 返回差集结果数组,键名也做比较
- array_intersect($arr1,$arr2); 返回交集结果数组
- array_intersect_assoc($arr1,$arr2); 返回交集结果数组,键名也做比较
其他的数组函数
- range(0,12); 创建一个包含指定范围单元的
- array_unique($arr); 移除数组中重复的值,新的数组中会保留原始的键名
- array_reverse($arr,TRUE); 返回一个单元顺序与原数组相反的数组,如果第二个参数为TRUE保留原来的键名
- array_rand($arr,2); 从数组中随机取出一个或 多个元素
-
shuffle($arr); 将数组的顺序打乱
php常用字符串函数
字符串转换类函数
- addcslashes:以C语言风格使用反斜线转义字符串中的
- addslashes:使用反斜线引用字符串
- chop:清除字符串中的连续空格
- get_html_translation_table:返回htmlspecialchars()函数和htmlentities()函数的转换表
- chunk_split函数:将字符串分割成小块
- hebrev函数:转换希伯来逻辑字符为可见字符
- hebrevc函数:转换希伯来语文本为可见文本,包括换行符
- html_entity_decode函数:转换HTML字符编码为字符
- htmlentities函数:转换字符为HTML字符编码
- htmlspecialchars_decode函数:转换特殊HTML字符编码为字符
- htmlspecialchars函数:转换特殊字符为HTML字符编码
- nl2br函数:将换行字符转换成
- quotemeta函数:加入引用符
- rtrim函数:清除字符串末尾的空白(或其他字符)
- strip_tags函数:清除HTML及PHP的标记
- stripcslashes函数:将用addslashes()函数处理后的字符串原样返回
- stripslashes函数:清除字符串中的反斜线
- strtolower函数:将字符串中的字母全部转换为小写
- strtoupper函数:将字符串中的字母全部转换为大写
- trim函数:截取字符串首尾的空格(或其他字符)字符串操作类函数
- explode函数:将字符串按指定字符切开
- str_pad函数:用一个字符串填充另一个字符串到指定长度
- str_split函数:把字符串转化为数组
- str_shuffle函数:随机打乱字符串中的字符顺序
- str_ireplace函数:将某个子字符串替换为另一个字符串
- localeconv函数:获取当地的数字和货币信息
- ltrim函数:清除字符串左端的连续空白(或者其他字符)
- money_format函数:把数字字符串转换为货币表示形式
- nl_langinfo函数:获取语言和地区信息
- number_format函数:用千位分隔符格式化数字字符串
- parse_str函数:将字符串解析为变量
- setlocale函数:设置地区信息
- similar_text函数:计算两个字符串中相匹配的字符串数量
- str_repeat函数:重复使用指定字符串
- str_replace函数:区分大小写的字符串替换
- str_word_count函数:返回字符串中的单词数
- strcasecmp函数:对两个字符串进行比较
- strlen函数:统计字符串的长度
- strnatcmp函数:使用自然顺序演算法来对比字符串
- strrev函数:颠倒字符串
- strtok函数:切开字符串
- strtr函数:转换字符串的某些字符
- substr_count函数:计算一个字符串在另一个字符串中出现的次数
- substr_replace函数:替换字符串中的一部分为另一个字符串
- substr函数:截取字符串
- ucfirst函数:将字符串中第一个字符改为大写
- ucwords函数:将字符串中每个单词的第一个字母改为大写
- wordwrap函数:将字符串按字符个数换行
字符串查找类函数
- chr函数:将指定的序数转化为相应的ASCII码字符
- implode函数:将数组合并为字符串
- join函数:将数组转化为字符串
- crc32函数:计算一个字符串的crc32多项式
- strchr函数:返回字符串在另一个字符串中首次出现的位置到末尾的子字符串
- strcspn函数:返回字符串中起首不符合mask的子字符串长度
- stripos函数:查找一个字符串在另一个字符串中首次出现的位置(不区分大小写)
- stristr函数:返回一个字符串在另一个字符串中首次出现的位置到后者末尾的子字符串(不区分大小写)
- strripos函数:不区分大小写查找字符在字符串中首次出现的位置
- strrpos函数:查找字符在字符串中最后出现的位置
- strspn函数:返回字符串中起首符合mask的子字符串长度
- strstr函数:返回字符串中某字符串开始处至结束的字符串
字符串编码类函数
- bin2hex函数:将二进制字符转化为十六进制
- convert_cyr_string函数:将字符由一种Cyrillic字符转换成另一种
- convert_uudecode函数:对用uuencode算法编码的字符串解码
- convert_uuencode函数:对字符串进行uuencode算法编码
- count_chars函数:返回字符串中所有字符的信息
- ord函数:返回一个字符的ASCII码
- str_rot13函数:对一个字符串进行rot13编码
字符串加密类函数
- crypt函数:将字符串用DES编码加密
- md5_file函数:计算给定文件的MD5hash
- md5函数:对一个字符串进行MD5加密
- sha1_file函数:计算给定文件的sha1hash
- password_hash 函数用于创建密码的散列(hash)
- password_verify 验证密码是否和散列值匹配
输入输出函数
- echo函数:输出一个或多个字符
- fprintf函数:输出格式化字符串到流
- print函数:输出格式化字符串
- printf函数:输出一个格式化的字符串
- sprintf函数:返回一个格式化的字符串到变量
- sscanf函数:按照一定格式解析输入的字符串
- vfprintf函数:输出格式化字符串到流
- vprintf函数:输出一个格式化的字符串
- vsprintf函数:输出格式化的字符串到变量
字符串比较类函数
- levenshtein函数:计算两个字符串的Levenshtein距离
- metaphone函数:计算字符串的metaphone键
- soundex函数:计算字符串的soundex键
- strcoll函数:对两个字符串进行比较
- strcmp函数:比较两个字符串
- strnatcasecmp函数:用自然运算法则比较字符串(不区分大小写)
- strncasecmp函数:比较字符串的前n个字符(不区分大小写)
- strncmp函数:比较两个字符串的前n个字符
- strpbrk函数:查找字符串中的一系列字符
- strpos函数:寻找字符串中某字符最先出现的位置
- strrchr函数:查找一个字符串在另一个字符串中末次出现的位置,并返回从字符串中的这个位置起,一直到字符串结尾的子字符串
- substr_compare函数:从指定起始位置起在一定长度内比较两个字符串(二进制精确,区分大小写可选)
PSR规范
PSR 是由 PHP FIG 组织制定的 PHP 规范,是 PHP 开发的实践标准。 文档地址 https://learnku.com/docs/psr - PSR0 自动加载已经废弃,psr4取代 - PSR1 基础编码规范 - PSR2 编程风格规范 - PSR3 日志接口规范 - PSR4 自动加载规范 - PSR6 缓存接口规范 - PSR7 HTTP消息接口规范 - PSR8 - PSR9 - PSR10 - PSR11 容器接口 - PSR12 编码规范补充 - PSR13 超媒体链接 - PSR14 事件分发器 - PSR15 HTTP请求处理器 - PSR16 缓存接口 - PSR17 HTTP工厂 - PSR18 HTTP客户端
PHP7新特性
- 标量类型声明
- 返回值类型声明
- NULL 合并运算符
<?php // 如果 $_GET['user'] 不存在返回 'nobody',否则返回 $_GET['user'] 的值 $username = $_GET['user'] ?? 'nobody'; // 类似的三元运算符 $username = isset($_GET['user']) ? $_GET['user'] : 'nobody';
- 太空船操作符(组合比较符)
//太空船操作符用于比较两个表达式。当$a大于、等于或小于$b时它分别返回-1、0或1。 <?php // 整型 echo 1 <=> 1; // 0 echo 1 <=> 2; // -1 echo 2 <=> 1; // 1 // 浮点型 echo 1.5 <=> 1.5; // 0 echo 1.5 <=> 2.5; // -1 echo 2.5 <=> 1.5; // 1 // 字符串 echo "a" <=> "a"; // 0 echo "a" <=> "b"; // -1 echo "b" <=> "a"; // 1
- 通过 define() 定义常量数组
<?php define('ANIMALS', [ 'dog', 'cat', 'bird' ]); echo ANIMALS[1]; // 输出 "cat"
- 匿名类
- Unicode codepoint 转译语法
- 整除
<?php var_dump(intdiv(10, 3)); // int(3)
- use 加强
use some\namespace\{ClassA, ClassB, ClassC as C}; use function some\namespace\{fn_a, fn_b, fn_c}; use const some\namespace\{ConstA, ConstB, ConstC};
- 一次捕捉多种类型的异常 / 错误
try { throw new LengthException("LengthException"); // throw new DivisionByZeroError("DivisionByZeroError"); // throw new Exception("Exception"); } catch (\DivisionByZeroError | \LengthException $e) { echo "出错消息 --- ", $e->getMessage(), PHP_EOL; } catch (\Exception $e) { echo "出错消息 --- ", $e->getMessage(), PHP_EOL; } finally { // ... }
- 可见性修饰符的变化
- 函数/方法:public、private、protected、abstract、final
- 类:abstract、final
- 属性/变量:public、private、protected
- 类常量:public、private、protected
PHP_FPM性能调优
- pm = dynamic: 子进程的数量根据以下配置动态设置
pm.max_children,
pm.start_servers,
pm.min_spare_servers,
pm.max_spare_servers - pm = ondemand: 进程在请求时按需创建,而不是动态的,其中 pm.start_servers 进程数量在服务启动时创建
- pm = static: 子进程的数量由 pm.max_children 决定
当流量波动比较大的时候,,PHP-FPM 的 ondemand 和 dynamic 会因为固有开销而限制吞吐量。 您需要了解您的系统并设置 PHP-FPM 进程数,以匹配服务器的最大容量。
从 pm.max_children 开始,根据 pm dynamic 或 ondemand 的最大使用情况去设置
您会注意到,在 pm static 模式下,因为您将所有内容都保存在内存中,所以随着时间的推移,流量峰值会对 CPU 造成比较小的峰值,并且您的服务器负载和 CPU 平均值将变得更加平滑。 每个需要手动调整的 PHP-FPM 进程数的平均大小会有所不同
Session 回收机制
session 存储方式:文件|memcache|redis|数据库
设定一个30分钟过期的session 如果设置key 过期时间30分钟即可
如果文件形式,php gc机制有一定的触发几率,30分钟临界点不一定会过期
session.save_handler= file session.gc_probability = 1 sesssion.gc_divisor = 1000 session.gc_maxfiletime= 1440 //设置过期时间24分钟 //过期的概率 session.gc_probability / session.gc_divisor session.save_path =
php垃圾回收机制
php变量底层定义
struct _zval_struct { union { long lval; double dval; struct { char *val; int len; } str; HashTable *ht; zend_object_value obj; } value; //变量value值 zend_uint refcount__gc; //引用计数内存中使用次数,为0删除该变量 zend_uchar type; //变量类型 zend_uchar is_ref__gc; //区分是否是引用变量 };
变量容器
- 非array和object变量
每次将常量赋值给一个变量时,都会产生一个变量容器
举例:
$a = '许铮的技术成长之路'; xdebug_debug_zval('a') a: (refcount=1, is_ref=0)='许铮的技术成长之路'
- array和object变量
会产生元素个数+1的变量容器
举例:
$b = [ 'name' => '许铮的技术成长之路', 'number' => 3 ]; b: (refcount=1, is_ref=0)=array ('name' => (refcount=1, is_ref=0)='许铮的技术成长之路', 'number' => (refcount=1, is_ref=0)=3) xdebug_debug_zval('b')
垃圾回收机制:
1、以php的引用计数机制为基础(php5.3以前只有该机制)
2、同时使用根缓冲区机制,当php发现有存在循环引用的zval时,就会把其投入到根缓冲区,当根缓冲区达到配置文件中的指定数量后,就会进行垃圾回收,以此解决循环引用导致的内存泄漏问题
函数 static 和 global 关键字的作用,如:
function foo() { static $a = 111; global $b }
static 是静态变量,在局部函数中存在且只初始化一次,使用过后再次使用会使用上次执行的结果
static 在类中是静态方法,不需要实例化可直接使用
global 关键字引用全局变量
$GLOBAL 在函数内使用具有全局作用域的变量,$GLOBAL['a']
子类重写父类的 protected 方法有什么限制?要遵守什么规则?
public 类内,类外,可以访问 protected 类内,子类的类内,可以访问 private 只有当前类的类内,可以访问 子类可以继承父类私有属性,不能继承私有方法 子类继承自父类的属性方法,子类必须和父类的修饰相同,或者更松弛。 父类为 public,则子类必须为 public。 父类为 protected,子类需为 public 或 protected。 父类为 private,则子类可为 public protected private
列举常用的设计模式并说明?单例模式,观察者模式等等
- 单例模式构造方法是私有的 - 实例用静态变量存储 - 不能序列化或克隆
final class Singleton{ private static $instance; private function __construct(){} public static function getInstance() { if(null === static::$instance){ static::$instance = new static(); } return static::$instance; } private function __clone(){} private function __wakeup(){} }
写一段代码,实现PHP内部的通知机制,如当一个类的属性发生变化时,另外一个类就可以收到通知。
观察者模式
抽象类和接口的区别
定义:
-
接口
- 接口是个集合,并不是类
- 接口中没有构造方法
- 接口中的方法必须是抽象的
- 接口中除了static、final变量,不能有其他变量
- 接口支持多继承(一个类可以实现多个接口)
-
抽象类
- 使用abstract修饰符修饰的类
- 一个抽象类不能实例化,因为“没有包含足够多的信息来描述一个具体的对象“
区别:
- 抽象类使用extends关键字来继承抽象类,子类使用关键字implements来实现接口
- 抽象类可以有构造方法,而接口不能有构造方法
- 抽象方法可以有public、protected和default这些修饰符,接口方法默认修饰符是public。不可以使用其它修饰符
- 抽象方法比接口速度要快(接口是稍微有点慢的,因为它需要时间去寻找在类中实现的方法)
- 抽象类中添加新的方法,可以给它提供默认的实现。因此你不需要改变你现在的代码。 如果在接口中添加方法,那么必须改变实现该接口的类
依赖注入、控制翻转
- 容器:字面上理解就是装东西的东西。常见的变量、对象属性等都可以算是容器。一个容器能够装什么,全部取决于你对该容器的定义
- IoC容器: 会根据类的依赖需求,自动在注册、绑定的一堆实例中搜寻符合的依赖需求,并自动注入到构造函数参数中去
- 这种自动搜寻依赖需求的功能,是通过 反射(Reflection) 实现的
- 服务提供者:我们需要某个服务,就得先注册、绑定这个服务到容器,那么提供服务并绑定服务至容器的东西就是 服务提供者(ServiceProvider)
进程、线程、协程
进程
进程是系统资源分配的最小单位, 系统由一个个进程(程序)组成 一般情况下,包括文本区域(text region)、数据区域(data region)和堆栈(stack region)。
- 文本区域存储处理器执行的代码
- 数据区域存储变量和进程执行期间使用的动态分配的内存;
- 堆栈区域存储着活动过程调用的指令和本地变量。
因此进程的创建和销毁都是相对于系统资源,所以是一种比较昂贵的操作。 进程有三个状态:
- 等待态:等待某个事件的完成;
- 就绪态:等待系统分配处理器以便运行;
- 运行态:占有处理器正在运行。
进程是抢占式的争夺CPU运行自身,而CPU单核的情况下同一时间只能执行一个进程的代码,但是多进程的实现则是通过CPU飞快的切换不同进程,因此使得看上去就像是多个进程在同时进行.
通信问题: 由于进程间是隔离的,各自拥有自己的内存内存资源, 因此相对于线程比较安全, 所以不同进程之间的数据只能通过 IPC(Inter-Process Communication) 进行通信共享.
线程
- 线程属于进程
- 线程共享进程的内存地址空间
- 线程几乎不占有系统资源
- 通信问题:进程相当于一个容器,而线程而是运行在容器里面的,因此对于容器内的东西,线程是共同享有的,因此线程间的通信可以直接通过全局变量进行通信,但是由此带来的例如多个线程读写同一个地址变量的时候则将带来不可预期的后果,因此这时候引入了各种锁的作用,例如互斥锁等。
同时多线程是不安全的,当一个线程崩溃了,会导致整个进程也崩溃了,即其他线程也挂了, 但多进程而不会,一个进程挂了,另一个进程依然照样运行。
- 进程是系统分配资源的最小单位
- 线程是CPU调度的最小单位
- 由于默认进程内只有一个线程,所以多核CPU处理多进程就像是一个进程一个核心
线程和进程的上下文切换
进程切换分3步:
- 切换页目录以使用新的地址空间
- 切换内核栈
- 切换硬件上下文
而线程切换只需要第2、3步,因此进程的切换代价比较大
协程
- 协程是属于线程的。协程程序是在线程里面跑的,因此协程又称微线程和纤程等
- 协没有线程的上下文切换消耗。协程的调度切换是用户(程序员)手动切换的,因此更加灵活,因此又叫用户空间线程.
- 原子操作性。由于协程是用户调度的,所以不会出现执行一半的代码片段被强制中断了,因此无需原子操作锁。
协程的实现:迭代器和生成器
- 迭代器: 实现了迭代接口的类,接口函数例如:current,key,next,rewind,valid。迭代器最基本的规定了对象可以通过next返回下一个值,而不是像数组,列表一样一次性返回。语言实现:在Java的foreach遍历迭代器对(数组),Python的for遍历迭代器对象(tuple,list,dist)。
- 生成器: 使用 yield 关键字的函数,可以多次返回值,生成器实际上也算是实现了迭代器接口(协议)。即生成器也可通过next返回下一个值。
协程举例:在Python中,使用了yield的函数为生成器函数,即可以多次返回值。则生成器可以暂停一下,转而执行其他代码,再回来继续执行函数往下的代码。
大数据量文件读取
使用yield迭代器
function getLine($fileName){ $f = fopen($fileName,'r'); try { while ($data = fgets($f)){ yield $data; } } finally { fclose($f); } } $mem = memory_get_usage(); $path = "./bbb/aaa.mp4"; foreach (getLine($path) as $k=>$v){ // } $memEnd = memory_get_usage(); echo ($memEnd - $mem) /1024/1024 . "MB";
断点续传原理及实现
主要依靠HTTP协议中 header HTTP_RANGE实现.
Http Header中 Range、Content-Range()
Http Header中一般断点下载时才用到Range和Content-Range实体头,
Range用户请求头中,指定第一个字节的位置和最后一个字节的位置,如(Range:200-300)
Content-Range用于响应头
举例:
<?php class FileDownload{ private $_speed = 512; // 下载速度 /**下载 * @param String $file 要下载的文件路径 * @param String $name 文件名称,为空则与下载的文件名称一样 * @param boolean $reload 是否开启断点续传 */ public function download($file, $name='', $reload=false){ if(file_exists($file)){ if($name==''){ $name = basename($file); } $fp = fopen($file, 'rb'); $file_size = filesize($file); $ranges = $this->getRange($file_size); header('cache-control:public'); header('content-type:application/octet-stream'); header('content-disposition:attachment; filename='.$name); if($reload && $ranges!=null){ // 使用续传 header('HTTP/1.1 206 Partial Content'); header('Accept-Ranges:bytes'); // 剩余长度 header(sprintf('content-length:%u',$ranges['end']-$ranges['start'])); // range信息 header(sprintf('content-range:bytes %s-%s/%s', $ranges['start'], $ranges['end'], $file_size)); // fp指针跳到断点位置 fseek($fp, sprintf('%u', $ranges['start'])); }else{ header('HTTP/1.1 200 OK'); header('content-length:'.$file_size); } while(!feof($fp)){ echo fread($fp, round($this->_speed*1024,0)); ob_flush(); } ($fp!=null) && fclose($fp); }else{ return ''; } } /**设置下载速度 * @param int $speed */ public function setSpeed($speed){ if(is_numeric($speed) && $speed>16 && $speed<4096){ $this->_speed = $speed; } } /**获取header range信息 * @param int $file_size 文件大小 * @return Array */ private function getRange($file_size){ if(isset($_SERVER['HTTP_RANGE']) && !empty($_SERVER['HTTP_RANGE'])){ $range = $_SERVER['HTTP_RANGE']; $range = preg_replace('/[\s|,].*/', '', $range); $range = explode('-', substr($range, 6)); if(count($range)<2){ $range[1] = $file_size; } $range = array_combine(array('start','end'), $range); if(empty($range['start'])){ $range['start'] = 0; } if(empty($range['end'])){ $range['end'] = $file_size; } return $range; } return null; } }
快排、冒泡、二分
- 快排
<?php function quickSort($arr){ $len = count($arr); if($len <= 1){ return $arr; } $v = $arr[0]; $low = $up = []; for ($i=0;$i < $len;$i++){ if($arr[$i] > $v){ $up[] = $v[$i]; }else{ $low[] = $v[$i]; } } $low = quickSort($low); $up = quickSort($up); return array_merge($low, array($v), $up); }
- 冒泡
循环比较|交换键值
function bubbleSort($arr){ $len = count($arr); for ($i= 0;$i<$len;$i++){ for ($j = $i+1;$j < $len;$j++){ if($arr[$i] < $arr[$j]){ list($arr[$j],$arr[$i]) = [$arr[$i],$arr[$j]]; } } } }
3.二分
二分查找也称折半查找(Binary Search),它是一种效率较高的查找方法。但是,折半查找要求线性表必须采用顺序存储结构,而且表中元素按关键字有序排列
- 确定要查找的区间
- 确定要二分时的参照点
- 区间内选取二分点
- 根据二分点的值,综合左右区间情况以及求解的目的,舍去一半无用的区间
- 继续在有效区间重复上面的步骤
/** * @param array $arr 待查找区间 * @param int $number 查找数 * @param int $lower 区间最低点 * @param int $high 区间最高点 * @return int */ function binarySearch(&$arr, $number, $lower, $high){ $middle = intdiv(($lower + $high) / 2); if($lower > $high) return -1; if($number > $arr[$middle]){ binarySearch($arr,$number,$middle+1,$high); }elseif ($number > $arr[$middle]){ binarySearch($arr,$number,$lower,$arr[$middle]-1); }else{ return $middle; } }
PHP文件末尾是否应该加 ?> 结束符号,为什么?
如果文件内容是纯 PHP 代码,最好在文件末尾删除 PHP
结束标记。这可以避免在 PHP 结束标记之后万一意外加入了空格或者换行符,会导致
PHP 开始输出这些空白,而脚本中此时并无输出的意图。
这些影响最多的时候应该是在使用 include 和 require的时候,加了结束标签 如果又在后面加了空格都有可能会引起多余的输出、php错误、之后的输出无法显示、空白页。因此,所有的php文件应该省略这个php闭合标签,并插入一段注释来标明这是文件的底部并定位这个文件在这个应用的相对路径。这样有利于你确定这个文件已经结束而不是被删节的。
谈一谈 PHP 开源框架 CI,ThinkPHP,Laravel 的优缺点及选型依据
CI 非常轻量级,是一个简单的MVC框架,性能也很快。
ThinkPHP3.2 国内使用比较多,优点是文档非常多,各种问题解决方案比较多,缺点是代码不够规范,理念落后。
Laravel 是一个现代化的PHP开发框架,代码优雅,使用 composer 方式扩展功能,社区活跃,缺点是比较重,比较适合做后台管理或者应用型WEB系统。
使用 PHP 下载网络图片,有哪些方法?
- 使用file_get_contents
- 使用curl
- 使用fopen
什么是 CGI?什么是 FastCGI?php-fpm,FastCGI,Nginx 之间是什么关系?
- CGI:是 Web Server 与 Web Application 之间数据交换的一种协议。
- FastCGI:同 CGI,是一种通信协议,但比 CGI 在效率上做了一些优化。同样,SCGI 协议与 FastCGI 类似。
- PHP-CGI:是 PHP (Web Application)对 Web Server 提供的 CGI 协议的接口程序。
- PHP-FPM:是 PHP(Web Application)对 Web Server 提供的 FastCGI 协议的接口程序,额外还提供了相对智能一些任务(fastcgi的进程管理器)
PHP读取文件的三种方法
fopen打开文件,fread全部读取内容
file_get_contents全部读取内容
CSRF攻击原理和防范
CSRF(Cross-site request forgery,跨站请求伪造),是通过伪造请求,冒充用户在站内进行操作 原因 CSRF 攻击之所以能够成功:是因为完全伪造用户的请求,该请求中所有的用户验证信息都是存在于 cookie 中,因此黑客可以在不知道这些验证信息的情况下直接利用用户自己的 cookie 来通过安全验证 解决办法:在请求中放入黑客所不能伪造的信息,并且该信息不存在于 cookie 之中 - 验证 HTTP Referer 字段 - 关键操作只接受POST请求,因为GET请求的参数携带在URL中,很容易进行模拟,而POST请求的参数在http body中 - 验证码,每次操作都需要用户进行互动,从而简单有效的防御了CSRF攻击,但是验证码太多,也会影响用户体验 - 可以在 HTTP 请求中以参数的形式加入一个随机产生的 token,并在服务器端来验证这个 token - 可以在 HTTP 头中自定义的属性里加入一个随机产生的 token,通过XMLHttpRequest,可以给所有请求加上这个token,通过XMLHttpRequest 请求的地址不会被记录到浏览器的地址栏,也不用担心 token 会透过 Referer 泄露到其他网站中 -
XSS攻击原理和防范
XSS(Cross Site Scripting,跨站脚本攻击),是注入攻击的一种,特点是不对服务器端造成任何伤害,而是通过一些正常的站内交互途径 所有可输入的地方没有对输入数据进行处理的话,都会存在XSS漏洞,防御 XSS 攻击最简单直接的方法,就是过滤用户的输入可以直接对用户的输入进行转义(htmlspecialchars|)
SQL注入攻击防范
1. 使用PDO预处理的方式,也就是一个萝卜一个坑 2. 使用htmlspecialchars()等函数进行转义。还可以对输入类型进行检测,类型转换。
为什么pdo预处理能正确处理sql注入呢
sql已经预编译好了,坑已经挖好了,来一个填一个,不会改变原本sql的意思,那么后面的or 1=1;--根本不会被mysql编译成执行计划,被当作普通的字符串处理
密码哈希
WebSocket连接过程
webSocket 是一个持久化协议,webSocket 是基于HTTP协议的,或者说 借用 HTTP的协议来完成一部分握手。
-
发送一个GET请求;header信息包含以下
- Upgrade: websocket
- Connection: Upgrade
- 服务器收到了协议,返回一个 Switching Protocol, 这样就连接成功了
- 接下来的通信都是websocket
HTTP常见状态码
- 100 Continue 继续。客户端应继续其请求 - 101 Switching Protocols 切换协议。服务器根据客户端的请求切换协议。只能切换到更高级的协议,例如,切换到HTTP的新版本协议 - 200 OK 请求成功。一般用于GET与POST请求 - 201 Created 已创建。成功请求并创建了新的资源 - 202 Accepted 已接受。已经接受请求,但未处理完成 - 203 Non-Authoritative Information 非授权信息。请求成功。但返回的meta信息不在原始的服务器,而是一个副本 - 204 No Content 无内容。服务器成功处理,但未返回内容。在未更新网页的情况下,可确保浏览器继续显示当前文档 - 205 Reset Content 重置内容。服务器处理成功,用户终端(例如:浏览器)应重置文档视图。可通过此返回码清除浏览器的表单域 - 206 Partial Content 部分内容。服务器成功处理了部分GET请求 - 300 Multiple Choices 多种选择。请求的资源可包括多个位置,相应可返回一个资源特征与地址的列表用于用户终端(例如:浏览器)选择 - 301 Moved Permanently 永久移动。请求的资源已被永久的移动到新URI,返回信息会包括新的URI, 浏览器会自动定向到新URI。今后任何新的请求都应使用新的URI代替 - 302 Found临时移动。与301类似。但资源只是临时被移动。客户端应继续使用原有URI - 303 See Other 查看其它地址。与301类似。使用GET和POST请求查看 - 304 Not Modified 未修改。所请求的资源未修改,服务器返回此状态码时,不会返回任何资源。客户端通常会缓存访问过的资源,通过提供一个头信息指出客户端希望只返回在指定日期之后修改的资源 - 305 Use Proxy 使用代理。所请求的资源必须通过代理访问 - 306 Unused 已经被废弃的HTTP状态码 - 307 Temporary Redirect 临时重定向。与302类似。使用GET请求重定向 - 400 Bad Request 客户端请求的语法错误,服务器无法理解 - 401 Unauthorized 请求要求用户的身份认证 - 402 Payment Required 保留,将来使用 - 403 Forbidden 服务器理解请求客户端的请求,但是拒绝执行此请求 - 404 Not Found 服务器无法根据客户端的请求找到资源(网页)。通过此代码,网站设计人员可设置"您所请求的资源无法找到"的个性页面 - 405 Method Not Allowed 客户端请求中的方法被禁止 - 406 Not Acceptable 服务器无法根据客户端请求的内容特性完成请求 - 407 Proxy Authentication Required 请求要求代理的身份认证,与401类似,但请求者应当使用代理进行授权 - 408 Request Time-out 服务器等待客户端发送的请求时间过长,超时 - 409 Conflict 服务器完成客户端的 PUT 请求时可能返回此代码,服务器处理请求时发生了冲突 - 410 Gone 客户端请求的资源已经不存在。410不同于404,如果资源以前有现在被永久删除了可使用410代码,网站设计人员可通过301代码指定资源的新位置 - 411 Length Required 服务器无法处理客户端发送的不带Content-Length的请求信息 - 412 Precondition Failed 客户端请求信息的先决条件错误 - 413 Request Entity Too Large 由于请求的实体过大,服务器无法处理,因此拒绝请求。为防止客户端的连续请求,服务器可能会关闭连接。如果只是服务器暂时无 法处理,则会包含一个Retry-After的响应信息 - 414 Request-URI Too Large 请求的URI过长(URI通常为网址),服务器无法处理 - 415 Unsupported Media Type 服务器无法处理请求附带的媒体格式 - 416 Requested range not satisfiable 客户端请求的范围无效 - 417 Expectation Failed 服务器无法满足Expect的请求头信息 - 500 Internal Server Error 服务器内部错误,无法完成请求 - 501 Not Implemented 服务器不支持请求的功能,无法完成请求 - 502 Bad Gateway 作为网关或者代理工作的服务器尝试执行请求时,从远程服务器接收到了一个无效的响应 - 503 Service Unavailable 由于超载或系统维护,服务器暂时的无法处理客户端的请求。延时的长度可包含在服务器的Retry-After头信息中 - 504 Gateway Time-out 充当网关或代理的服务器,未及时从远端服务器获取请求 - 505 HTTP Version not supported 服务器不支持请求的HTTP协议的版本,无法完成处理
三次握手 4次挥手
网络7层模型
OSI 模型是从上往下的,越底层越接近硬件,越往上越接近软件,这七层模型分别是物理层、数据链路层、网络层、传输层、会话层、表示层、应用层
- 物理层:底层数据传输,如网线;网卡标准。
- 数据链路层:定义数据的基本格式,如何传输,如何标识;如网卡MAC地址。
- 网络层:定义IP编址,定义路由功能;如不同设备的数据转发。
- 传输层:端到端传输数据的基本功能;如 TCP、UDP。
- 会话层:控制应用程序之间会话能力;如不同软件数据分发给不同软件。
- 标识层:数据格式标识,基本压缩加密功能。
- 应用层:各种应用软件,包括 Web 应用
redis常见数据结构及使用场景
* String类型是二进制安全的,可以用来缓存一些静态文件,如图片、视频、css文件等。支持incr操作,可以用作计数器,比如统计网站访问次数等。 * 微博中“关注、粉丝”、论坛中所有回帖的ID用的就是list列表,还有消息队列,也是列表。 * Set 可以快速查找元素是否存在,用于记录一些不能重复的数据。例如:在网站注册账号时,用户名不能重复,使用Set记录注册用户,如果注册的用户名已经存在于Set中,就拒绝该用户注册。或者用于记录做过某些事情。例如: 在某些投票系统中,每个用户一天只能投票一次,就可以用Set来记录某个用户的投票情况。 * Set能做的事Sorted Set也能做,Sorted Set还能完成一些Set不能做的事情。例如:使用Sorted Set构建一个具有优先级的队列。 * Hash适用于存储对象,比如把用户的信息存到hash里,以用户id为key,用户的详细信息为value。
redis数据结构的底层实现
Memcache 和 Redis 的读写性能(qps)如何?两者优缺点?Redis 支持哪些数据类型?Redis 如何持久化?
读写性能:memcache更加快速,在读取性能上比 Redis 快,缺点是仅支持字符串。 Redis支持丰富的数据结构类型,字符串,散列(哈希),集合,有序集合,还支持订阅发布,地理位置等等。 实际运用中可以redis,memcache结合,memcache可作为session存储的方式,session都是KV类型键值对。 Redis 提供了多种不同级别的持久化方式: 1、RDB 持久化可以在指定的时间间隔内生成数据集的时间点快照(point-in-time snapshot)。 2、AOF 持久化记录服务器执行的所有写操作命令,并在服务器启动时,通过重新执行这些命令来还原数据集。 AOF 文件中的命令全部以 Redis 协议的格式来保存,新命令会被追加到文件的末尾。 Redis 还可以在后台对 AOF 文件进行重写(rewrite),使得 AOF 文件的体积不会超出保存数据集状态所需的实际大小。 3、Redis 还可以同时使用 AOF 持久化和 RDB 持久化。 在这种情况下, 当 Redis 重启时, 它会优先使用 AOF 文件来还原数据集, 因为 AOF 文件保存的数据集通常比 RDB 文件所保存的数据集更完整。
nginx重写
rewrite
模块允许使用正则修改请求的URI,发起内部跳转再匹配location,或者直接做30x重定向返回客户端。
rewrite regex replacement [flag] 复制代码
其中的regex是PCRE风格的正则,rewrite的运行规则如下
* 如果regex匹配当前请求的uri,则replacement 会被当作是新的uri参与后续处理。 * 如果在server级别设置该选项,那么他们将在location之前生效。 * 如果新URI字符中有关于协议的任何东西,比如http://或者https://等,则终止处理并直接响应302 * 如果同一个上下文中(server、location、if)有多个能够匹配uri的rewrite正则,则会根据rewrite指令出现的先后顺序连续进行重写替换,并将替换后的replacement当作新的uri参与后续处理 * 如果想要终止匹配,可以使用第三个参数flag,其取值如下 * `last`表示停止处理任何rewrite相关的指令,并用替换后的uri开始下一轮的location匹配 * `break`表示停止处理任何rewrite相关的指令,且直接使用该uri来处理请求,不再进行location匹配 * `redirect`如果不包含协议,且是一个新的uri,则用新的uri匹配的location去处理请求,不会进行30x跳转;但是他也可以直接返回30x,让浏览器自己进行重定向请求 * `permanent`与`redirect`相同,区别在于它是直接返回301永久重定向
需要注意的是last
和break
的区别,如果在location中使用location,则会再次以新的URI重新发起重定向,并再次进行location匹配,如果新的uri和旧的uri都再次匹配到一个相同的location,就会发生死循环,当这种循环超过10次时,nginx就会返回500。因此牢记:在server上下文中使用last,而在location上下文中使用break。
接下来让我们通过一些例子来验证rewrite的规则。
server { listen 80; server_name test2.com; index index.html; root /Users/Txm/nginx_test/; access_log /usr/local/etc/nginx/logs/test2.access.log; error_log /usr/local/etc/nginx/logs/error2.log error; rewrite ^/baidu http://www.baidu.com; rewrite ^/bai http://image.baidu.com; return 403; }