PHP 面试知识点整理归纳

全文已整理补充完毕,以后还会继续更新文章里面的错误,以及补充尚不完善的问题。


该篇文章是针对Github上wudi/PHP-Interview-Best-Practices-in-China资源的答案个人整理

lz也是初学者,以下知识点均为自己整理且保持不断更新,也希望各路大神多多指点,若发现错误或有补充,可直接comment,lz时刻关注着。

由于内容比较多,没有直接目录,请自行对照 Github;没有仔细整理格式,各位见谅见谅!

 

基础篇

了解大部分数组处理函数

  • array_chunk — 将一个数组分割成多个
  • array_column — 返回数组中指定的一列
  • array_combine — 创建一个数组,用一个数组的值作为其键名,另一个数组的值作为其值(另一种意义的合并数组)
  • array_pop — 弹出数组最后一个单元(出栈)
  • array_push — 将一个或多个单元压入数组的末尾(入栈)
  • array_rand — 从数组中随机取出一个或多个单元
  • array_search — 在数组中搜索给定的值,如果成功则返回首个相应的键名
  • array_shift — 将数组开头的单元移出数组
  • arsort — 对数组进行逆向排序并保持索引关系
  • asort — 对数组进行排序并保持索引关系
  • count — 计算数组中的单元数目,或对象中的属性个数
  • current — 返回数组中的当前单元
  • in_array — 检查数组中是否存在某个值
  • krsort — 对数组按照键名逆向排序
  • ksort — 对数组按照键名排序
  • list — 把数组中的值赋给一组变量
  • sort — 对数组排序
  • uasort — 使用用户自定义的比较函数对数组中的值进行排序并保持索引关联
  • uksort — 使用用户自定义的比较函数对数组中的键名进行排序
  • usort — 使用用户自定义的比较函数对数组中的值进行排序

字符串处理函数 区别 mb_ 系列函数

  • chunk_split — 将字符串分割成小块
  • explode — 使用一个字符串分割另一个字符串
  • implode — 将一个一维数组的值转化为字符串
  • lcfirst — 使一个字符串的第一个字符小写
  • ltrim — 删除字符串开头的空白字符(或其他字符)
  • md5 — 计算字符串的 MD5 散列值
  • money_format — 将数字格式化成货币字符串
  • nl2br — 在字符串所有新行之前插入 HTML 换行标记
  • number_format — 以千位分隔符方式格式化一个数字
  • ord — 返回字符的 ASCII 码值
  • rtrim — 删除字符串末端的空白字符(或者其他字符)
  • str_replace — 子字符串替换
  • str_ireplace — str_replace 的忽略大小写版本
  • str_repeat — 重复一个字符串
  • str_shuffle — 随机打乱一个字符串
  • str_split — 将字符串转换为数组
  • stripos — 查找字符串首次出现的位置(不区分大小写)
  • strpos — 查找字符串首次出现的位置
  • strstr — 查找字符串的首次出现
  • stristr — strstr 函数的忽略大小写版本
  • strlen — 获取字符串长度
  • strrchr — 查找指定字符在字符串中的最后一次出现
  • strrev — 反转字符串
  • strripos — 计算指定字符串在目标字符串中最后一次出现的位置(不区分大小写)
  • strrpos — 计算指定字符串在目标字符串中最后一次出现的位置
  • strtok — 标记分割字符串
  • strtolower — 将字符串转化为小写
  • strtoupper — 将字符串转化为大写
  • substr_count — 计算字串出现的次数
  • substr_replace — 替换字符串的子串
  • substr — 返回字符串的子串
  • trim — 去除字符串首尾处的空白字符(或者其他字符)
  • ucfirst — 将字符串的首字母转换为大写
  • ucwords — 将字符串中每个单词的首字母转换为大写
  • wordwrap — 打断字符串为指定数量的字串

普通字符串处理函数和mb_系列函数的区别:

不同编码的个别语言(比如中文)所占字节数不同,一个汉字在GB2312编码下占2个字节,在UTF-8(是变长编码)编码下占2-3个字节,普通字符串处理函数是按每个字符1字节来处理的,而mb_系列的函数在使用时可以多指定一个编码参数,方便处理不同编码的中文。

最简单的例子,strlen()会返回一个字符串所占字节数,而mb_strlen()会返回一个字符串的字符数。再比如,substr($str2, 2, 2)$str为中文时可能会正好截取到一个汉字的一部分,这时就会发生乱码,而mb_substr($str, 2, 2, ‘utf-8’)指定编码后就不会发生乱码问题了,中文时即是取几个汉字。

& 引用,结合案例分析

PHP 的引用允许用两个变量来指向同一个内容。

$a =& $b;

$a 和 $b 在这里是完全相同的,这并不是 $a 指向了 $b 或者相反,而是 $a 和 $b 指向了同一个地方;

 

引用做的第二件事是用引用传递变量

function foo(&$var)
{    $var++; }
$a=5;
foo($a);

将使 $a 变成 6。这是因为在 foo 函数中变量 $var 指向了和 $a 指向的同一个内容。

 

引用不是指针,下面的结构不会产生预期的效果:

function foo(&$var)

{    $var =& $GLOBALS["baz"];    }

foo($bar);

 

当 unset 一个引用,只是断开了变量名和变量内容之间的绑定。这并不意味着变量内容被销毁了。例如:

$a 1;
$b =& $a;
unset($a);

不会 unset  $b,只是 $a。

==  === 区别

简单来说,==是不带类型比较是否相同(比如数字100 == ‘100’结果为true),===是带类型比较是否相同(比如100 == ‘100’结果为false),官方手册的解释也类似:

 

isset  empty 区别

看到一个简洁代码的解释:

 

再具体说:$a不存在和$a = null 两种情况在isset看来为true,其余为false(包括$a = ‘’;)

$a = null, 0, false, ‘ ’, 或不存在时在empty看来为true,其余为false。

再多说一句,isset用来判断变量是否存在;empty用来判断变量是否有值;这里要特别注意0这个值在某些表单验证情况下可能是有效值,此时不能仅用empty判断变量是否有值,需要另作处理。

全部魔术函数理解

  1. __construct 类的构造函数,常用来给类的属性赋值,注意事项:

如果子类中定义了构造函数则不会隐式调用其父类的构造函数。要执行父类的构造函数,需要在子类的构造函数中调用 parent::__construct()。如果子类没有定义构造函数则会如同一个普通的类方法一样从父类继承(假如没有被定义为 private 的话)

  1. __destruct 析构函数,析构函数会在到某个对象的所有引用都被删除或者当对象被显式销毁时执行。
  2. __call__callStatic 在对象中调用一个不可访问方法时,__call() 会被调用。在静态上下文中调用一个不可访问方法时,__callStatic() 会被调用,作为调用类中不存在的方法时对开发者的一个友好提示
  3. __set__get__isset __unset 在给不可访问属性赋值时,__set() 会被调用;读取不可访问属性的值时,__get() 会被调用;当对不可访问属性调用 isset()  empty() 时,__isset() 会被调用;当对不可访问属性调用 unset() 时,__unset() 会被调用。
  4. __sleep__wakeup  serialize() 函数会检查类中是否存在一个魔术方法 __sleep()。如果存在,该方法会先被调用,然后才执行序列化操作。此功能可以用于清理对象,并返回一个包含对象中所有应被序列化的变量名称的数组。如果该方法未返回任何内容,则 NULL 被序列化,并产生一个 E_NOTICE 级别的错误.返回父类的私有成员的名字,常用于提交未提交的数据,或类似的清理操作;与之相反,unserialize() 会检查是否存在一个 __wakeup() 方法。如果存在,则会先调用 __wakeup 方法,预先准备对象需要的资源。__wakeup() 经常用在反序列化操作中,例如重新建立数据库连接,或执行其它初始化操作
  5. __toString 用于当直接echo $obj(一个对象)时该显示什么内容,必须返回一个字符串且不能在方法内抛出异常
  6. __invoke  当尝试以调用函数的方式调用一个对象时,__invoke() 方法会被自动调用例如function __invoke($x) {  var_dump($x); } $obj = new CallableClass; $obj(5);会输出int(5)
  7. __set_state 调用 var_export() 导出类时,此静态 方法会被调用。本方法的唯一参数是一个数组,其中包含按 array('property' => value, ...) 格式排列的类属性
  8. __clone  对象复制可以通过 clone 关键字来完成(如果可能,这将调用对象的 __clone() 方法)。对象中的 __clone() 方法不能被直接调用。
  9. $copy_of_object = clone $object; 当对象被复制后,PHP 5 会对对象的所有属性执行一个浅复制(shallow copy)。所有的引用属性 仍然会是一个指向原来的变量的引用。当复制完成时,如果定义了 __clone() 方法,则新创建的对象(复制生成的对象)中的 __clone() 方法会被调用,可用于修改属性的值(如果有必要的话)。
  1. __debugInfo 当var_dumo(new Class)(参数为一个对象时),该方法可以控制显示的内容,若没有定义此方法,var_dump()将默认展示对象的所有属性和方法

static$thisself 区别

$this通俗解释就是当前类的一个实例,不必多说,主要是static::和self::的区别

class A {

    public static function className(){

        echo __CLASS__;

    }

 

    public static function test(){

        self::className();

 }}

class B extends A{

    public static function className(){

        echo __CLASS__;

 }}

B::test();

这将打印出来A

另一方面static::它具有预期的行为

class A {

    public static function className(){

        echo __CLASS__;

    }

 

    public static function test(){

        static::className();

 }}

class B extends A{

    public static function className(){

        echo __CLASS__;

 }}

B::test();

这将打印出来B

在PHP 5.3.0中称为后期静态绑定。它解决了调用运行时引用的类的限制。

privateprotectedpublicfinal 区别

public:权限是最大的,可以内部调用,实例调用等。

protected: 受保护类型,用于本类和继承此类的子类调用。

private: 私有类型,只有在本类中使用。

static:静态资源,可以被子类继承。

abstract:修饰抽象方法,没有方法体,由继承该类的子类来实现。

final:表示该变量、该方法已经“完成”,不可被覆盖。修饰类时该类不能被继承。 

(因此final和abstract不能同时出现)

OOP 思想

简单理解:

面向对象的编程就是编出一个人来,这个人可以做很多种动作,跑,跳,走,举手...他能做什么取决于你如何组合这些动作,有些动作在一些功能中是不用的。

而层次化的编程(面向过程)就是造出一个具体的工具,他只能干这样一件事,条件——结果。

抽象类、接口 分别使用场景

接口通常是为了抽象一种行为,接口是一种规范,在设计上的意义是为了功能模块间的解耦,方便后面的功能扩展、维护,接口不能有具体的方法;

抽象类可以有具体的方法,也可以有抽象方法,一旦一个类有抽象方法,这个类就必须声明为抽象类,很多时候是为子类提供一些共用方法;

所以,抽象类是为了简化接口的实现,他不仅提供了公共方法的实现,让你可以快速开发,又允许你的类完全可以自己实现所有的方法,不会出现紧耦合的问题。

应用场合很简单了

1 优先定义接口

2 如果有多个接口实现有公用的部分,则使用抽象类,然后集成它。

举个简单的例子:有一个动物接口,内有动物叫声和动物说你好两个方法,在实现该接口时各个动物的叫声肯定是不同的,但是他们都在说你好是相同的,此时就可以用抽象类,把相同的说你好的方法抽象出去,就不用在每个动物类中写了。

Trait 是什么东西

Trait 是为类似 PHP 的单继承语言而准备的一种代码复用机制。Trait 为了减少单继承语言的限制,使开发人员能够自由地在不同层次结构内独立的类中复用 method。Trait 和 Class 组合的语义定义了一种减少复杂性的方式,避免传统多继承和 Mixin 类相关典型问题。

Trait 和 Class 相似,但仅仅旨在用细粒度和一致的方式来组合功能。 无法通过 trait 自身来实例化。它为传统继承增加了水平特性的组合;也就是说,应用的几个 Class 之间不需要继承。

简单理解:Trait为不支持多继承的php实现了多继承,使用时不是用extends继承,而是在类内部用 use 类名 表示。

重名方法优先级问题:当前类的成员覆盖 trait 的方法,而 trait 则覆盖被继承的方法。

echoprintprint_r 区别(区分出表达式与语句的区别)

Echo,print是语言结构,print_r和var_dump是常规功能

print并且echo或多或少相同; 它们都是显示字符串的语言结构。差异很微妙:print返回值为1,因此可以在表达式中使用,但echo具有void返回类型; echo可以采用多个参数,尽管这种用法很少见; echoprint快一点。(就个人而言,我总是使用echo,从不print。)

var_dump打印出变量的详细转储,包括其类型大小和任何子项的类型和大小(如果它是数组或对象)。

print_r以更易于阅读的格式化形式打印变量数组或对象):不能传递字符串,省略类型信息,不给出数组大小等。

var_dump print_r根据我的经验,通常调试时更有用。当您不确切知道变量中的值/类型时,它尤其有用。考虑这个测试程序:

$values = array(0, 0.0, false, '');

var_dump($values);

print_r ($values);

随着print_r你不能告诉之间的区别00.0,或false''

array(4) {

  [0]=>  int(0)

  [1]=>  float(0)

  [2]=>  bool(false)

  [3]=>  string(0) ""

}

Array(

    [0] => 0

    [1] => 0

    [2] =>

    [3] => )

__construct  __destruct 区别

在一个类中定义一个方法作为构造函数。具有构造函数的类会在每次创建新对象时先调用此方法,所以非常适合在使用对象之前做一些初始化工作。

析构函数会在到某个对象的所有引用都被删除或者当对象被显式销毁时执行。和构造函数一样,父类的析构函数不会被引擎暗中调用。要执行父类的析构函数,必须在子类的析构函数体中显式调用 parent::__destruct()。此外也和构造函数一样,子类如果自己没有定义析构函数则会继承父类的。析构函数即使在使用 exit() 终止脚本运行时也会被调用。在析构函数中调用 exit() 将会中止其余关闭操作的运行。

static 作用(区分类与函数内)手册 SOF

声明类属性或方法为静态,就可以不实例化类而直接访问。静态属性不能通过一个类已实例化的对象来访问(但静态方法可以)。

为了兼容 PHP 4,如果没有指定访问控制,属性和方法默认为公有。

由于静态方法不需要通过对象即可调用,所以伪变量 $this 在静态方法中不可用。

静态属性不可以由对象通过 -> 操作符来访问但可以由对象通过 :: 来访问

用静态方式调用一个非静态方法会导致一个 E_STRICT 级别的错误。

就像其它所有的 PHP 静态变量一样,静态属性只能被初始化为文字或常量,不能使用表达式。所以可以把静态属性初始化为整数或数组,但不能初始化为另一个变量或函数返回值,也不能指向一个对象。

可以用一个值等于类名的字符串变量来动态调用类。但该变量的值不能为关键字 selfparent 或 static比如有个class A{}, 则可以用$a=A; $a::这样调用

在类之外(即:在函数中),static变量是在函数退出时不会丢失其值的变量。在同一函数的不同调用中维护的变量只有一个值。从PHP手册的例子:

function test(){

    static $a = 0;

    echo $a;

    $a++;}

test();  // prints 0

test();  // prints 1

test();  // prints 2

__toString() 作用

用于一个类被当成字符串时应怎样回应。例如 echo $obj; ($obj为一个对象) 应该显示些什么。此方法必须返回一个字符串,否则将发出一条E_RECOVERABLE_ERROR 级别的致命错误。类似与Java的toString方法

单引号'与双引号"区别

  1. 单引号字符串几乎完全“按原样”显示。变量和大多数转义序列都不会被解释。例外情况是,要显示单引号字符必须使用反斜杠\'转义它,显示反斜杠字符必须使用另一个反斜杠转义它\\
  2. 双引号字符串将显示一系列转义字符(包括一些正则表达式),并且将解析字符串中的变量。这里重要的一点是,您可以使用花括号来隔离要解析的变量的名称。例如,假设您有变量$type,那么您echo "The $type are"将查找该变量$type。绕过这个用途echo "The {$type} are"您可以在美元符号之前或之后放置左括号。看一下字符串解析,看看如何使用数组变量等。
  3. Heredoc字符串语法就像双引号字符串一样。它始于<<<。在此运算符之后,提供标识符,然后提供换行符。字符串本身如下,然后再次使用相同的标识符来关闭引号。您不需要在此语法中转义引号。
  4. Nowdoc(自PHP 5.3.0开始)字符串语法基本上类似于单引号字符串。不同之处在于,甚至不需要转义单引号或反斜杠。nowdoc用与heredocs相同的<<<序列标识,但后面的标识符用单引号括起来,例如<<<'EOT'在nowdoc中没有解析。

常见 HTTP 状态码,分别代表什么含义301 什么意思 404 ?

  1. 1xx消息:这一类型的状态码,代表请求已被接受,需要继续处理。由于HTTP/1.0协议中没有定义任何1xx状态码,所以除非在某些试验条件下,服务器禁止向此类客户端发送1xx响应。
  2. 2xx成功:这一类型的状态码,代表请求已成功被服务器接收、理解、并接受
  3. 200 OK:请求已成功,请求所希望的响应头或数据体将随此响应返回。实际的响应将取决于所使用的请求方法。在GET请求中,响应将包含与请求的资源相对应的实体。在POST请求中,响应将包含描述或操作结果的实体
  4. 202 Accepted:服务器已接受请求,但尚未处理。最终该请求可能会也可能不会被执行,并且可能在处理发生时被禁止。
  5. 204 No Content:服务器成功处理了请求,没有返回任何内容
  6. 3xx重定向:这类状态码代表需要客户端采取进一步的操作才能完成请求。通常,这些状态码用来重定向,后续的请求地址(重定向目标)在本次响应的Location域中指明。
  7. 301 Moved Permanently:被请求的资源已永久移动到新位置,并且将来任何对此资源的引用都应该使用本响应返回的若干个URI之一。如果可能,拥有链接编辑功能的客户端应当自动把请求的地址修改为从服务器反馈回来的地址。除非额外指定,否则这个响应也是可缓存的。新的永久性的URI应当在响应的Location域中返回。除非这是一个HEAD请求,否则响应的实体中应当包含指向新的URI的超链接及简短说明。如果这不是一个GET或者HEAD请求,那么浏览器禁止自动进行重定向,除非得到用户的确认,因为请求的条件可能因此发生变化。注意:对于某些使用HTTP/1.0协议的浏览器,当它们发送的POST请求得到了一个301响应的话,接下来的重定向请求将会变成GET方式。
  8. 4xx客户端错误:这类的状态码代表了客户端看起来可能发生了错误,妨碍了服务器的处理。除非响应的是一个HEAD请求,否则服务器就应该返回一个解释当前错误状况的实体,以及这是临时的还是永久性的状况。这些状态码适用于任何请求方法。浏览器应当向用户显示任何包含在此类错误响应中的实体内容
  9. 400 Bad Request:由于明显的客户端错误(例如,格式错误的请求语法,太大的大小,无效的请求消息或欺骗性路由请求),服务器不能或不会处理该请求
  10. 401 Unauthorized:类似于403 Forbidden,401语义即“未认证”,即用户没有必要的凭据。[32]该状态码表示当前请求需要用户验证。该响应必须包含一个适用于被请求资源的WWW-Authenticate信息头用以询问用户信息。客户端可以重复提交一个包含恰当的Authorization头信息的请求。
  11. 403 Forbidden:服务器已经理解请求,但是拒绝执行它。与401响应不同的是,身份验证并不能提供任何帮助,而且这个请求也不应该被重复提交。如果这不是一个HEAD请求,而且服务器希望能够讲清楚为何请求不能被执行,那么就应该在实体内描述拒绝的原因。当然服务器也可以返回一个404响应,假如它不希望让客户端获得任何信息。
  12. 404 Not Found:请求失败,请求所希望得到的资源未被在服务器上发现,但允许用户的后续请求。[35]没有信息能够告诉用户这个状况到底是暂时的还是永久的。假如服务器知道情况的话,应当使用410状态码来告知旧资源因为某些内部的配置机制问题,已经永久的不可用,而且没有任何可以跳转的地址。404这个状态码被广泛应用于当服务器不想揭示到底为何请求被拒绝或者没有其他适合的响应可用的情况下。
  13. 405 Method Not Allowed:请求行中指定的请求方法不能被用于请求相应的资源。
  14. 408 Request Timeout:请求超时
  15. 5xx服务器错误:表示服务器无法完成明显有效的请求。[56]这类状态码代表了服务器在处理请求的过程中有错误或者异常状态发生,也有可能是服务器意识到以当前的软硬件资源无法完成对请求的处理。除非这是一个HEAD请求,否则服务器应当包含一个解释当前错误状态以及这个状况是临时的还是永久的解释信息实体。浏览器应当向用户展示任何在当前响应中被包含的实体。这些状态码适用于任何响应方法
  16. 500 Internal Server Error:通用错误消息,服务器遇到了一个未曾预料的状况,导致了它无法完成对请求的处理。没有给出具体错误信息
  17. 502 Bad Gateway:作为网关或者代理工作的服务器尝试执行请求时,从上游服务器接收到无效的响应
  18. 503 Service Unavailable:由于临时的服务器维护或者过载,服务器当前无法处理请求。这个状况是暂时的,并且将在一段时间以后恢复。
  19. 504 Gateway Timeout:作为网关或者代理工作的服务器尝试执行请求时,未能及时从上游服务器(URI标识出的服务器,例如HTTP、FTP、LDAP)或者辅助服务器(例如DNS)收到响应。注意:某些代理服务器在DNS查询超时时会返回400或者500错误。

 

 

进阶篇

AutoloadComposer 原理 PSR-4 原理

Autoload机制可以使得PHP程序有可能在使用类时才自动包含类文件,而不是一开始就将所有的类文件include进来,这种机制也称为lazy loading(懒加载)

function __autoload($classname)

{

  $classpath="./".$classname.'.class.php';

  if(file_exists($classpath))

  {    require_once($classpath);  }

  else

  {    echo 'class file'.$classpath.'not found!';   }

}

 $person = new Person(”Altair”, 6);

 var_dump ($person);

通常PHP5在使用一个类时,如果发现这个类没有加载,就会自动运行__autoload()函数,在这个函数中我们可以加载需要使用的类。autoload至少要做三件事情,第一件事是根据类名确定类文件名,第二件事是确定类文件所在的磁盘路径(在我们的例子是最简单的情况,类与调用它们的PHP程序文件在同一个文件夹下),第三件事是将类从磁盘文件中加载到系统中(php7.2废除__autoload函数,建议使用spl_autoload_register() 实现相同功能)

Autoload原理简单概述:

1.检查执行器全局变量函数指针autoload_func是否为NULL。

2.如果autoload_func==NULL, 则查找系统中是否定义有__autoload()函数,如果没有,则报告错误并退出。

3.如果定义了__autoload()函数,则执行__autoload()尝试加载类,并返回加载结果。

4.如果autoload_func不为NULL,则直接执行autoload_func指针指向的函数用来加载类。注意此时并不检查__autoload()函数是否定义。

spl_autoload_register() 就是我们上面所说的__autoload调用堆栈,我们可以向这个函数注册多个我们自己的 autoload() 函数,当 PHP 找不到类名时,PHP就会调用这个堆栈,然后去调用自定义的 autoload() 函数,实现自动加载功能。如果我们不向这个函数输入任何参数,那么就会默认注册 spl_autoload() 函数

Composer 做了哪些事情:你有一个项目依赖于若干个库;其中一些库依赖于其他库;你声明你所依赖的东西;Composer 会找出哪个版本的包需要安装,并安装它们(将它们下载到你的项目中)。

执行 composer require 时发生了什么:composer 会找到符合 PR4 规范的第三方库的源;将其加载到 vendor 目录下;初始化顶级域名的映射并写入到指定的文件里;写好一个 autoload 函数,并且注册到 spl_autoload_register()里。

Composer是利用的遵循psr-4规范的类自动加载机制实现的,PSR-4规范简介:

  1. 完整的类名 必须 要有一个顶级命名空间,被称为 "vendor namespace";
  2. 完整的类名 可以 有一个或多个子命名空间;
  3. 完整的类名 必须 有一个最终的类名;
  4. 完整的类名中任意一部分中的下滑线都是没有特殊含义的;
  5. 完整的类名 可以 由任意大小写字母组成;
  6. 所有类名都 必须 是大小写敏感的。
  1. 完整的类名中,去掉最前面的命名空间分隔符,前面连续的一个或多个命名空间和子命名空间,作为「命名空间前缀」,其必须与至少一个「文件基目录」相对应;
  2. 紧接命名空间前缀后的子命名空间 必须 与相应的「文件基目录」相匹配,其中的命名空间分隔符将作为目录分隔符。
  3. 末尾的类名 必须 与对应的以 .php 为后缀的文件同名。
  4. 自动加载器(autoloader)的实现 一定不可 抛出异常、一定不可 触发任一级别的错误信息以及 不应该 有返回值。

Composer自动加载原理概述:

如果我们在代码中写下 new phpDocumentor\Reflection\Element(),PHP 会通过 SPL_autoload_register 调用 loadClass -> findFile -> findFileWithExtension。步骤如下:

 

将 \ 转为文件分隔符/,加上后缀php,变成 $logicalPathPsr4, 即 phpDocumentor/Reflection//Element.php;

利用命名空间第一个字母p作为前缀索引搜索 prefixLengthsPsr4 数组,查到下面这个数组:

        p' =>

            array (

                'phpDocumentor\\Reflection\\' => 25,

                'phpDocumentor\\Fake\\' => 19,

          )

遍历这个数组,得到两个顶层命名空间 phpDocumentor\Reflection\ 和 phpDocumentor\Fake\

在这个数组中查找 phpDocumentor\Reflection\Element,找出 phpDocumentor\Reflection\ 这个顶层命名空间并且长度为25。

在prefixDirsPsr4 映射数组中得到phpDocumentor\Reflection\ 的目录映射为:

    'phpDocumentor\\Reflection\\' =>

        array (

            0 => __DIR__ . '/..' . '/phpdocumentor/reflection-common/src',

            1 => __DIR__ . '/..' . '/phpdocumentor/type-resolver/src',

            2 => __DIR__ . '/..' . '/phpdocumentor/reflection-docblock/src',

        ),

遍历这个映射数组,得到三个目录映射;

查看 “目录+文件分隔符//+substr($logicalPathPsr4, $length)”文件是否存在,存在即返回。这里就是

'__DIR__/../phpdocumentor/reflection-common/src + substr(phpDocumentor/Reflection/Element.php,25)'

如果失败,则利用 fallbackDirsPsr4 数组里面的目录继续判断是否存在文件

 

Session 共享、存活时间

为什么要使用Session共享:

分布式开发项目中,用户通过浏览器登录商城,实际上会被转发到不同的服务器,当用户登录进入服务器A,session保存了用户的信息,用户再次点击页面被转发到服务器B,这时问题来了,服务器B没有该用户的session信息,无法验证通过,用户被踢回到登录页面,这样体验效果非常不好,甚至无法验证用户,购物车里面商品都不存在了。

利用Redis实现简单的Session共享:

 

  1. 用户第一次进入商城首页,给一个CSESSIONID,(不用JSESSIONID的原因),用户添加商品,各种需要记录的操作,都与这个CSESSIONID关联起来;
  2. 当使用登录操作时候,将这个用户的信息,如用户名等存入到redis中,通过K_V,将CSESSIONID加一个标志作为key,将用户信息作为value;
  3. 当用户点击页面被转发到其他服务器时候,在需要验证是否同一个用户时,就可以从redis中取出value,进行验证用户信息,实现共享。

Session 在php配置文件中的默认有效时间是24分钟,设置session永久有效的方法:

如果拥有服务器的操作权限,那么只需要进行如下的步骤:
    1、把“session.use_cookies”设置为1,打开Cookie储存SessionID,不过默认就是1,一般不用修改;
    2、把“session.cookie_lifetime”改为正无穷(当然没有正无穷的参数,不过999999999和正无穷也没有什么区别);
    3、把“session.gc_maxlifetime”设置为和“session.cookie_lifetime”一样的时间;

 

异常处理

异常处理用于在指定的错误(异常)情况发生时改变脚本的正常流程。这种情况称为异常。

异常的简单使用:

抛出一个异常throw new Exception("Value must be 1 or below"),同时不去捕获它服务器会报Fatal error: Uncaught exception 'Exception' 的错误;

抛出一个异常throw new Exception("Value must be 1 or below")并try{} catch(Exception $e){echo:Message: . $e->getMessage();},当异常发生时,服务器就会报预设的错误提示:Message: Value must be 1 or below

自定义Exception类:必须继承Exception类,可以使用Exception类的所有方法:

class customException extends Exception

 {

 public function errorMessage()

  {

  //error message

  $errorMsg = 'Error on line '.$this->getLine().' in '.$this->getFile()

  .': <b>'.$this->getMessage().'</b> is not a valid E-Mail address';

  return $errorMsg;

  }

 }

异常的规则:

  • 需要进行异常处理的代码应该放入 try 代码块内,以便捕获潜在的异常。
  • 每个 try 或 throw 代码块必须至少拥有一个对应的 catch 代码块。
  • 使用多个 catch 代码块可以捕获不同种类的异常。
  • 可以在 try 代码块内的 catch 代码块中再次抛出(re-thrown)异常。

简而言之:如果抛出了异常,就必须捕获它

 

如何 foreach 迭代对象

展示foreach工作原理的例子:

class myIterator implements Iterator {

    private $position = 0;

    private $array = array(

        "firstelement",

        "secondelement",

        "lastelement",

    );  

 

    public function __construct() {

        $this->position = 0;

    }

//返回到迭代器的第一个元素

    function rewind() {

        var_dump(__METHOD__);

        $this->position = 0;

    }

// 返回当前元素

    function current() {

        var_dump(__METHOD__);

        return $this->array[$this->position];

    }

//返回当前元素的键

    function key() {

        var_dump(__METHOD__);

        return $this->position;

    }

//向前移动到下一个元素

    function next() {

        var_dump(__METHOD__);

        ++$this->position;

    }

//检查当前位置是否有效

    function valid() {

        var_dump(__METHOD__);

        return isset($this->array[$this->position]);

    }

}

 

$it = new myIterator;

 

foreach($it as $key => $value) {

    var_dump($key, $value);

    echo "\n";

}

 

输出结果:

string(18) "myIterator::rewind"

string(17) "myIterator::valid"

string(19) "myIterator::current"

string(15) "myIterator::key"

int(0)

string(12) "firstelement"

 

string(16) "myIterator::next"

string(17) "myIterator::valid"

string(19) "myIterator::current"

string(15) "myIterator::key"

int(1)

string(13) "secondelement"

 

string(16) "myIterator::next"

string(17) "myIterator::valid"

string(19) "myIterator::current"

string(15) "myIterator::key"

int(2)

string(11) "lastelement"

 

string(16) "myIterator::next"

string(17) "myIterator::valid"

 

如何数组化操作对象$obj[key];

PHP提供了ArrayAccess接口使实现此接口的类的实例可以向操作数组一样通过$obj[key]来操作,以下是php手册中对实现ArrayAccess接口的类的示例:

class obj implements arrayaccess {
    private $container = array();
    public function __construct() {
        $this->container = array(
            "one"   => 1,
            "two"   => 2,
            "three" => 3,
        );
    }

//设置一个偏移位置的值
    public function offsetSet($offset$value) {
        if (is_null($offset)) {
            $this->container[] = $value;
        } else {
            $this->container[$offset] = $value;
        }
    }

//检查一个偏移位置是否存在
    public function offsetExists($offset) {
        return isset($this->container[$offset]);
    }

//复位一个偏移位置的值
    public function offsetUnset($offset) {
        unset($this->container[$offset]);
    }

//获取一个偏移位置的值
    public function offsetGet($offset) {
        return isset($this->container[$offset]) ? $this->

container[$offset] : null;
    }
}

对该类测试使用:

$obj = new obj;
var_dump(isset($obj["two"]));
var_dump($obj["two"]);
unset($obj["two"]);
var_dump(isset($obj["two"]));
$obj["two"] = "A value";
var_dump($obj["two"]);
$obj[] = 'Append 1';
$obj[] = 'Append 2';
$obj[] = 'Append 3';
print_r($obj);
?>

以上例程的输出类似于:

bool(true)

int(2)

bool(false)

string(7) "A value"

obj Object

(

    [container:obj:private] => Array

        (

            [one] => 1

            [three] => 3

            [two] => A value

            [0] => Append 1

            [1] => Append 2

            [2] => Append 3

        )

 

)

 

如何函数化对象$obj(123);

利用PHP提供的魔术函数__invoke()方法可以直接实现,当尝试以调用函数的方式调用一个对象时,__invoke() 方法会被自动调用,下面是官方手册示例:

class CallableClass

{

    function __invoke($x) {

        var_dump($x);

    }

}

$obj = new CallableClass;

$obj(5);

var_dump(is_callable($obj));

输出结果:

int(5)

bool(true)

 

yield 是什么,说个使用场景 yield

PHP官方手册对yield的解释:

它最简单的调用形式看起来像一个return申明,不同之处在于普通return会返回值并终止函数的执行,而yield会返回一个值给循环调用此生成器的代码并且只是暂停执行生成器函数。

我的简单理解:yield起一个暂停程序的作用,比如在一个循环中,程序执行遇到yield语句就会返回yield声明的数据,而不是循环完整体返回,加了yield后就会挨个返回。

Caution:如果在一个表达式上下文(例如在一个赋值表达式的右侧)中使用yield,你必须使用圆括号把yield申明包围起来。 例如这样是有效的:$data = (yield $value);

 

属于PHP生成器语法,官方手册的解释:

一个生成器函数看起来像一个普通的函数,不同的是普通函数返回一个值,而一个生成器可以yield生成许多它所需要的值。

当一个生成器被调用的时候,它返回一个可以被遍历的对象.当你遍历这个对象的时候(例如通过一个foreach循环),PHP 将会在每次需要值的时候调用生成器函数,并在产生一个值之后保存生成器的状态,这样它就可以在需要产生下一个值的时候恢复调用状态。

一旦不再需要产生更多的值,生成器函数可以简单退出,而调用生成器的代码还可以继续执行,就像一个数组已经被遍历完了。

Note:一个生成器不

  • 29
    点赞
  • 80
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值