8天学会PHP之day2 PHP深入

 

这一章通过错误与异常、生成器、引用、预定义变量、上下文选项和参数、协议、路由与伪静态、事件、信号和进程等十个方面深入理解PHP。

一、错误与异常 Error&Exception

每一门语言的错误与异常都有自己的思考和思想。

像C语言的数组下标越界,程序仍然能正常执行这个问题,被很多人所诟病,认为C语言应该将其列为错误,并中止程序。但其本质是C语言设计之初没有考虑到广大用户的平均水平。

Java在这一点上做出了改进,添加了数组下标越界这个异常。

在Golang的设计中,程序没有error,只有状态。所以也有很多人诟病,因为他们的程序中充满了 if(:err != nil){}这种代码。

PHP是怎样处理的呢?

PHP分为错误处理Error和异常处理Exception

在PHP7之前,一般都使用Exception。后来PHP社区认为Exception没有太多意义,所以在PHP7以后,大多数错误几乎都被视为Error。所以我们主要来看Error。

抛出异常

抛出异常的常见方式有三种:

  • die()exit()

  • throw

  • trigger_error

捕获异常

捕获异常的常见方式有两种:

  • try/catch

  • set_error_handlerset_exception_handler

try/catch

由于Error类与Exception类没有继承关系,不可以用Error类去捕获Exception,反之亦然。

<?php

try {
   throw new Error('hi error');
} catch (Error $e) {
   echo 'error:', $e;
};
echo '<br/>';
try {
   throw new Exception('hi exception');
} catch (Exception $e) {
   echo 'exception:', $e;
}

set_error_handler()和set_exception_handler()会注册一个函数,来接管错误处理。

<?php

function wa_error(Error $e)
{
   echo 'error:', $e;
}

set_error_handler('wa_error');
throw new Error('hi error');

自定义的错误处理函数,还可以接受4个参数。分别是错误类型、错误消息、错误文件、错误行号。和JavaScript中的onerror极为相似。

function wa_error($error_type,$error_message,$error_file,$error_line)
{
   echo $error_type,$error_message,$error_file,$error_line;
}

错误分类

常见错误主要有三类:

  1. 语法错误

  2. 运行时错误

  3. 逻辑错误(这类错误引擎并不会帮我们做出提示,因为程序运行本身没有出错。)

错误级别

错误的级别分四级:

级别 说明 触发原因
deprecated 最低级错误 使用了过时的函数或语法
notice 通知级别错误 语法不当,如数组索引没加引号
warning 警告级别错误 语法不当,如参数不匹配
fatal error 致命错误,触发后程序无法继续执行 语法解析错误
prase error 解析错误 最高级别的语法解析错误

PHP的错误级别多达十几种之多,但我们只需要关心以上五种就够了。

fatal error

前三种级别的错误都可以被捕捉,但fatal error无法捕捉。针对这种情况,需要使register_shutdown_function();它会再程序终止或者die时触发,给程序最后一点事件。概念类似于JavaScript DOM中的unload和onbeforeunload。

parse error

发生parse error时,连register_shutdown_function也无能为力。只能通过记录日志来查看错误。这时需要修改php.ini文件中的配置。

log_error=On
error_log=usr/log/php.log

除此之外还有很多其他选项,更多信息可以查阅官方文档

 

二、生成器 generators

生成器的概念在很多语言中都有,比如ECMAScript6中就有。

PHP的生成器是在PHP5.5之后才引入进来的。

生成器的本质是提供了一种更容易的方法来实现简单的对象迭代。生成器往往会和迭代器 Iterator 一起出现。

什么是迭代器?在JavaScript中,凡是可以被循环的对象,都是迭代器的实例。这个概念拿到PHP中一样通用。也就是说,可以使用生成器来创建可循环(迭代)的对象,而不一定是数组。

<?php

function gen()
{
   $i = 1;
   for (; $i < 10; $i++) {
       yield $i.'<br/>';// yield 关键字会产生一个值。下次再调用时,会继续在上次yield的地方运行。
  }
}

foreach (gen() as $i) {
   echo $i;
}

在PHP中,生成器最重要的意义就是性能会很好,节省大量内存

凡是一个函数返回一个数组,而你又需要遍历这个数组时,都可以使用生成器来代替。

应用场景:在读取大文件时比较有用。

 

三、引用

PHP中引用就是不同的变量名访问相同的变量内容。这和JavaScript中的引用是一个概念,但JavaScript更加复杂一些。

两个变量同时指向一个内存地址。

<?php
$a =& $b;

注意:引用不是指针。

引用传递

通过在函数的参数前面加&符号。只允许三种方式。

  • 变量

  • new

  • 函数返回的引用

引用返回

下面的例子中,可以发现,这种方式和Vue.js中的数据双向绑定非常相似。

<?php

class foo
{
   public $value = 42;
// getValue 函数所返回的对象的属性将被赋值,而不是拷贝,就和没有用引用语法一样。
   public function &getValue()
  {
       return $this->value;
  }
}

$obj = new foo;
$myValue = &$obj->getValue(); // $myValue is a reference to $obj->value, which is 42.
echo $myValue . '<br/>';
$obj->value = 2;
echo $myValue . '<br/>';                // prints the new value of $obj->value, i.e. 2.

取消引用

使用unset 函数。

<?php
$a = 1;
$b =& $a;
unset($a);
?>

这样会断开变量 $a 和 变量内容 1 的联系。完全不会影响 $b 或者 1.

 

四、预定义变量

最新版本的PHP中大约有如下这些预定于变量。

其中最常用到的有$GLOBALS、$_SERVER、$FILES这几个。

 

五、上下文选项和参数

PHP 提供了多种上下文选项和参数,可用于所有的文件系统或数据流封装协议。

创建上下文

stream_context_create

设置上下文

stream_context_set_option

设置参数

stream_context_set_params

参数列表

  • Socket context options — Socket上下文选项列表

  • HTTP context options — HTTP上下文选项列表

  • FTP context options — FTP上下文选项列表

  • SSL context options — SSL上下文选项列表

  • CURL context options — CURL 上下文选项列表

  • Phar 上下文(context)选项 — Phar 上下文(context)选项列表

  • Context 参数 — Context 参数列表

 

六、协议

协议是每一门编程语言都必有的,它决定了语言能够连接什么。

PHP支持的协议类型:

  • file:// — 访问本地文件系统

  • http:// — 访问 HTTP(s) 网址

  • ftp:// — 访问 FTP(s) URLs

  • php:// — 访问各个输入/输出流(I/O streams)

  • zlib:// — 压缩流

  • data:// — 数据(RFC 2397)

  • glob:// — 查找匹配的文件路径模式

  • phar:// — PHP 归档

  • ssh2:// — Secure Shell 2

  • rar:// — RAR

  • ogg:// — 音频流

  • expect:// — 处理交互式的流

自定义协议类型

可以看到,PHP原生是没有对websocket协议和https协议进行支持的。但PHP提供了stream_wrapper_register函数,可以让用户自定义协议类型,PHP协议有一些是伪协议。什么是伪协议?写过JavaScript的同学应该看过javascript:viod(0)这种代码吧,这就是伪协议,语言自己定义的。

相关配置

关于支持协议的相关配置,在php.ini文件中有对应的选项。

  • allow_url_fopen :on 默认开启 该选项为on便是激活了 URL 形式的 fopen 封装协议使得可以访问 URL 对象文件等。

  • allow_url_include:off 默认关闭,该选项为on便是允许 包含URL 对象文件等

allow_url_include依赖allow_url_fopen,如果不开启allow_url_fopen,allow_url_include就无法使用。

File协议

file协议是每一门服务端语言或建立在操作系统之上的语言都有的。file协议读取文件都是本地的,非常快。它不受allow_url_fopen与allow_url_include的影响。

语法:file:// [文件的绝对路径和文件名]

PHP协议

php协议用于访问输入流/输出流。

 

七、伪静态路由

这一节应该放在PHP扩展里面讲的,但为了凑十个小节,就拿到这里来了。

什么是伪静态路由?

伪静态就是让动态的URL地址看起来是静态的地址,伪静态是最终的目的,而不是技术。

举个例子:

在浏览器中输入

http://localhost/test/index1.html

http://localhost/test/index2.html

http://localhost/test/index3.html

都会转向 http://localhost/test/index.php 页面

并且依次转到

http://localhost/test/index1.html

http://localhost/test/index2.html

http://localhost/test/index3.html

但页面会显示不同的内容。

为什么需要伪静态?

因为很多地址都是动态的,会包含很多参数,这样地址栏的链接就会很长。

拿百度一个链接举例:https://www.baidu.com/s?ie=utf-8&f=8&rsv_bp=1&tn=99323164_hao_pg&wd=PHP%E4%BC%AA%E9%9D%99%E6%80%81%E8%B7%AF%E7%94%B1&oq=%25E8%25B7%25AF%25E7%2594%25B1%25E4%25B8%258E%25E4%25BC%25AA%25E9%259D%2599%25E6%2580%2581&rsv_pq=f3a24b2d0055c1a1&rsv_t=e5c6Fp4ohJhblq4VkcTMCIaYKczrCE5UbAxdY6B%2F0HGdCSa9ASGuCy1B08DtZpQhGglSIKg7&rqlang=cn&rsv_enter=1&rsv_dl=tb&inputT=6334&rsv_sug3=33&rsv_sug1=11&rsv_sug7=100&rsv_n=2&rsv_sug2=0&rsv_sug4=7083

这样有什么问题呢?首先这很难看,其次对搜索引擎不友好。

有哪些实现技术?

  1. web主机rewrite模块(URL重写,rewrite翻译成中文为重写,但很多人也常称伪静态)

  2. pathinfo (主机支持的一种技术,index.php/path,再利用url重写可以隐藏入口)

  3. 程序的路由(可以美化,缩短url,变得更人性化,更有语义)

受篇幅限制,这里就不挨个细说了。仅讲一下apache服务器的配置。

Apache实现伪静态

共三步。

1.将配置文件中httpd.conf #LoadModule rewrite_module modules/mod_rewrite前面的注释去掉。

2.在httpd.conf中添加

RewriteEngine On

\#RewriteCond %{ENV:SCRIPT_URL} (?:index|dispbbs)[-0-9]+.html

RewriteRule ^(.*?(?:index|dispbbs))-([-0-9]+).html 1.php?__is_apache_rewrite=1&__rewrite_arg=2

3.在<IfModule mod_rewrite.c></IfModule>之间添加如下配置。

RewriteMap tolowercase int:tolowerRewriteCond %{QUERY_STRING} (?:boardid|page|id|replyid|star|skin)=d+ [NC]RewriteRule ^(.*(?:index|dispbbs)).asp 1.php?{tolowercase:%{QUERY_STRING}}&__is_apache_rewrite=1

 

八、事件

什么是事件?

首先说一下Web服务端程序的原理,这和其他类型的计算机程序是不同的,有一些计算机程序执行完毕就会退出。但Web服务端程序大都会监听一个端口,并且卡在这里,使程序处于静止状态,等接收到http请求后,调动可用资源,执行相关任务,不停的重复此类行为。

我们可以把任何Web服务端程序都理解为两个状态。一个是“就绪”状态,一个是“执行”状态。

什么都不做的时候,就是“就绪”状态。执行任务的时候,就是“执行”状态。

那怎样让程序从“就绪”状态切换到“执行”状态呢?通过事件来处理的语言,就称为事件驱动型语言。最典型的就是JavaScript。

很可惜,PHP没有内置事件。所以很多框架自己实现了事件。

实现事件的原理并不复杂,一般使用观察者模式或者发布订阅模式来实现。发布订阅模式和观察者模式没有本质的区别,只是加入了消息队列。

这是一组对比图,可以看一下。

为了便于理解,我们可以自己写一个观察者模式的事件类。

<?php

interface Subject
{
   public function fireEvent();

   public function addObserver(Observer $observer);

   public function removeObserver(Observer $observer);
}

interface Observer
{
   public function subscribe(Subject $subject);

   public function unsubscribe(Subject $subject);

   public function update();
}

class EventSubject implements Subject
{
   private $container = [];

   public function fireEvent()
  {
       foreach ($this->container as $observer) {
           echo var_dump($observer);
           echo $observer->update()."<br/>";
      }
  }

   public function addObserver(Observer $observer)
  {
           array_push($this->container, $observer);

  }

   public function removeObserver(Observer $observer)
  {
       $key = array_search($observer, $this->container);
       array_splice($this->container, $key, 1);
  }
}

class  EventObserver implements Observer
{
   public function subscribe(Subject $subject)
  {
       $subject->addObserver($this);
  }

   public function unsubscribe(Subject $subject)
  {
       $subject->removeObserver($this);
  }

   public function update()
  {
       echo '事件触发';
  }
}

$eventSub = new EventSubject();
$eventOb1 = new EventObserver();
$eventOb2 = new EventObserver();
$eventOb3 = new EventObserver();
$eventOb1->subscribe($eventSub);
$eventOb2->subscribe($eventSub);
$eventOb3->subscribe($eventSub);
$eventSub->fireEvent();

观察者模式的本质是一种一对多的关系。当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。

 

九、信号

信号的本质:进程间通信。

信号是事件发生时对进程的通知机制,有时又称为软件中断。一个进程可以向另一个进程发送信号,比如子进程结束时都会向父进程发送一个SIGCHLD(17号信号)来通知父进程,所以有时信号也被当作一种进程间通信的机制。

PHP的 pcntl 扩展以及 posix 扩展为我们提供了若干操作信号的方法。

由于扩展安装出现一些小问题,这块没办法继续进行,明天补回来。

 

十、进程

首先明确一下进程的概念。

进程:一个具有一定独立功能的程序关于某个数据集合的一次运行活动,是系统进行资源分配和调度运行的基本单位。

进程号:就是PID,每个进程都有一个独一无二的进程号。

进程有什么用?处理一些耗时任务,一堆进程一起干,会很快。

PHP进程需要 pcntl 扩展支持。

进程的编程难度还是较高的,很容易出现问题。这里简单说几个概念。

孤儿进程:父进程死了,但子进程还活着。这些子进程会被一个进程号为1的init进程收养。

僵尸进程:一个进程通过fork创建子进程,子进程退出,父进程没有获取子进程状态,那么子进程的进程号还存在系统中。这样就会一直占用着进程号,如果有大量僵尸进程存在,把系统所有能用的进程号全部占用了,就会造成无法产生新进程的情况。

PHP进程的概念还是比较多的,更多的内容我也没来得及仔细研究,这里仅进行初步探索,较深入的知识以后有机会再细学。

由于扩展安装出现一些小问题,这块没办法继续进行,明天补回来。

猜你喜欢

转载自www.cnblogs.com/luzhenqian/p/11415717.html