Php工作模型和运行机制

PHP的工作模型非常特殊。从某种程度上说,PHP和ASP、ASP.NET、JSP/Servlet等流行的Web技术,有着本质上的区别。

   以Java为例,Java在Web应用领域,有两种技术:Java Servlet和JSP(Java Server Page)。Java Servlet是一种特殊类型的Java程序,它通过实现相关接口,处理Web服务器发送过来的请求,完成相应的工作。JSP在形式上是一种类似于PHP 的脚本,但是事实上,它最后也被编译成Servlet。

也就是说,在Java解决方案中,JSP和Servlet是作为独立的Java应用程序执行的,它们在初始化之后就驻留内存,通过特定的接口和 Web服务器通信,完成相应工作。除非被显式地重启,否则它们不会终止。因此,可以在JSP和Servlet中使用各种缓存技术,例如数据库连接池。

   ASP.NET的机制与此类似。至于ASP,虽然也是一种解释型语言,但是仍然提供了Application对象来存放应用程序级的全局变量,它依托于ASP解释器在IIS中驻留的进程,在整个应用程序的生命期有效。

   PHP却完全不是这样。作为一种纯解释型语言,PHP脚本在每次被解释时进行初始化,在解释完毕后终止运行。这种运行是互相独立的,每一次请求都会创建一个单独的进程或线程,来解释相应的页面文件。页面创建的变量和其他对象,都只在当前的页面内部可见,无法跨越页面访问旧电脑回收。

在终止运行后,页面中申请的、没有被代码显式释放的外部资源,包括内存、数据库连接、文件句柄、Socket连接等,都会被强行释放。
   也就是说,PHP无法在语言级别直接访问跨越页面的变量,也无法创建驻留内存的对象。见下例:
  
   <?php
   class StaticVarTester {
   public static $StaticVar = 0;
   }
  
   function TestStaticVar() {
   StaticVarTester :: $StaticVar += 1;
   echo "StaticVarTester :: StaticVar = " . StaticVarTester :: $StaticVar;
   }
  
   TestStaticVar();
   echo "<br/>";
   TestStaticVar();
   ?>
  
  在这个例子中,定义了一个名为StaticVarTester的类,它仅有一个公共的静态成员$StaticVar,并被初始化为0。然后,在 TestStaticVar()函数中,对StaticVarTester :: $StaticVar进行累加操作,并将它打印输出。
  熟悉 Java或C++的开发者对这个例子应该并不陌生。$StaticVar作为StaticVarTester类的一个静态成员,只在类被装载时进行初始化,无论StaticVarTester类被实例化多少次,$StaticVar都只存在一个实例,而且不会被多次初始化。因此,当第一次调用 TestStaticVar()函数时,$StaticVar进行了累加操作,值为1,并被保存。第二次调用TestStaticVar()函数,$ StaticVar的值为2。
   打印出来的结果和我们预料的一样:
  
   StaticVarTester :: StaticVar = 1
   StaticVarTester :: StaticVar = 2
  
   但是,当浏览器刷新页面,再次执行这段代码时,不同的情况出现了。在Java或C++里面,$StaticVar的值会被保存并一直累加下去,我们将会看到如下的结果:
  
   StaticVarTester :: StaticVar = 3
   StaticVarTester :: StaticVar = 4
   …
  
   但是在PHP中,由于上文叙及的机制,当前页面每次都解释时,都会执行一次程序初始化和终止的过程。也就是说,每次访问时,StaticVarTester都会被重新装载,而下列这行语句
  
   public static $StaticVar = 0;
  
  也会被重复执行。当页面执行完成后,所有的内存空间都会被回收,$StaticVar这个变量(连同整个StaticVarTester类)也就不复存在。因此,无论刷新页面多少次,$StaticVar变量都会回到起点:先被初始化为0,然后在TestStaticVar()函数调用中被累加。所以,我们看到的结果永远是这个:
  
   StaticVarTester :: StaticVar = 1
   StaticVarTester :: StaticVar = 2
PHP这种独特的工作模型的优势在于,基本上解决了令人头疼的资源泄漏问题。Web应用的特点是大量的、短时间的并发处理,对各种资源的申请和释放工作非常频繁,很容易导致泄漏。

同时,大量的动态html脚本的存在,使得追踪和调试的工作都非常困难。PHP的运行机制,以一种非常简单的方式避免了这个问题,同时也避免了将程序员带入到繁琐的缓冲池和同步等问题中去。在实践中,基于PHP的应用往往比基于Java或.NET的应用更加稳定,不会出现由于某个页面的BUG而导致整个站点崩溃的问题。

(相比之下,Java或.NET应用可能因为缓冲池崩溃或其他的非法操作,而导致整个站点崩溃。)后果是,即使PHP程序员水平不高,也无法写出使整个应用崩溃的代码。PHP脚本可以一次调用极多的资源,从而导致页面执行极为缓慢,但是执行完毕后所有的资源都会被释放,应用仍然不会崩溃。

甚至即使执行了一个死循环,也会在30秒(默认时间)后因为超时而中止。从理论上来说,基于PHP的应用是不太可能崩溃的,因为它的运行机制决定它不存在常规的崩溃这个问题。在实践中,很多开发者也认为PHP是最稳定的Web应用。

 但是,这种机制的缺点也非常明显。最直接的后果是,PHP在语言级别无法实现跨页面的缓冲机制。这种缓冲机制缺失造成的影响,可以分成两个方面:

  一是对象的缓冲。如我们所知,很多设计模式都依赖于对象的缓冲机制,对于需要频繁应付大量并发的服务端软件更是如此。因此,对象缓冲的缺失,理论上会极大地降低速度。但是,由于PHP本身的定位和工作机制等原因,它在实际工作中的速度非常快。就作者自己的经验来看,在小型的Web应用中,PHP至少不比 Java慢。

在大型的应用中,为了榨干每一分硬件资源,即使PHP本身足够快,一个优秀的对象缓冲机制仍然是必要的。在这种情况下,可以使用第三方的内存缓冲软件,如Memcached。由于Memcached本身的优异特性(高性能,支持跨服务器的分布式存储,和PHP的无缝集成等),在大型的PHP应用中, Memcached几乎已经成为不可或缺的基础设施了。比起使用PHP语言自己实现对象缓冲来,这种第三方解决方案似乎更好一些。

   二是数据库连接的缓冲。对MySQL,PHP提供了一种内置的数据库缓冲机制,使用起来非常简单,程序员需要做的只是用mysql_pconnect()代替mysql_connect()来打开数据库而已。

PHP会自动回收被废弃的数据库连接,以供重复使用。具有讽刺意味的是,在实际应用中,这种持久性数据库连接往往会导致数据库连接的伪泄漏现象:在某个时间,并发的数据库连接过多,超过了MySQL的最大连接数,从而导致新的进程无法连接数据库。

但是过一段时间,当并发数减少时,PHP会释放掉一些连接,网站又会恢复正常。出现这种现象的原因是,当使用pconnect时,Apache的httpd 进程会不释放connect,而当Apache的httpd进程数超过了mysql的最大连接数时,就会出现无法连接的情况。因此,需要小心地调整 Apache和Mysql的配置,以使Apache的httpd进程数不会超出MySQL的最大连接数。在某些情况下,一些有经验的PHP程序员宁可继续使用mysql_connect(),而不是mysql_pconnect()。

  就作者所知,在PHP未来的roadmap中,对于工作模型这一部分,没有根本性的变动。这是PHP的缺点,也是PHP的优势,从本质上说,这就是PHP 的独特之处。因此,我们很难期待PHP在近期内会对这一问题做出重大的改变。但是,在对待这个问题带来的一系列后果时,我们必须谨慎应对旧电脑回收。
  
   数据库访问接口
  长期以来,PHP都缺乏一个象ADO或JDBC那样的统一的数据库访问接口。PHP在访问不同的数据库时,使用不同的专门API。例如,使用 mysql_connect函数连接MySQL,使用ora_logon函数连接Oracle。平心而论,这种方式并没有象我们想象的那样麻烦。
在真实项目中,把系统从一种数据库完全迁移到另一种数据库的要求是比较少见的,特别是对于LAMP这样的小型项目而言。而且,只要将访问数据库的代码进行了良好的封装,迁移的工作量也会较少。另外,使用专门API,在效率上多少会有一些优势。

<think>嗯,用户想了解PHP运行机制原理。首先,我需要回忆一下PHP的基本工作流程。PHP是一种服务器端脚本语言,所以它通常在Web服务器上执行,生成动态网页内容。那PHP是如何从代码变成用户看到的页面的呢? 可能要从请求处理开始讲起。当用户访问一个PHP页面时,服务器接收到请求,然后交给PHP解释器处理。中间是不是有模块或者CGI的过程?比如Apache的mod_php或者Nginx通过FastCGI与PHP-FPM通信?这部分需要解释清楚不同服务器环境下的处理方式差异。 然后,PHP代码是如何被解析执行的?应该提到Zend引擎,它是PHP的核心,负责将PHP代码转换成抽象语法树(AST),然后生成操作码(opcode),最后执行这些操作码。解释型语言编译型的区别在这里可能也需要简单提及,比如PHP是解释执行,每次请求都会重新解析,除非用了opcache这样的缓存机制。 接下来,可能需要提到PHP的生命周期。从模块初始化、请求处理到关闭模块的各个阶段。比如每个请求开始时的初始化,执行代码,然后释放资源。还有像常驻进程的PHP-FPM如何管理多个请求,避免每次都要重新初始化的开销。 另外,内存管理也是重要的一环。PHP使用引用计数垃圾回收机制来自动管理变量内存,避免内存泄漏。这部分可能需要举例说明变量的创建销毁过程。 还有,PHP与扩展的交互。扩展是如何加载的,比如通过php.ini配置加载的模块,扩展如何挂钩到Zend引擎,提供额外的功能。例如,常见的MySQL扩展或PDO是如何工作的。 用户可能还想知道为什么PHP适合Web开发,比如它的无共享架构,每个请求都是独立的,减少了上下文切换的复杂性。或者像超全局变量$_GET、$_POST的处理流程,请求是如何被解析并注入到这些变量中的。 需要注意的是,用户可能有不同的服务器环境,比如使用Apache还是Nginx,或者不同的PHP版本(如PHP5 vs PHP7/8的性能改进)。可能需要提到SAPI(Server API)的不同,比如CLI模式下的运行机制与Web模式有何不同。 最后,可能还需要简要介绍PHP的优化手段,比如opcode缓存(OPcache)如何提升性能,以及JIT(Just-In-Time编译)在PHP8中的引入,如何进一步加速执行效率。 在组织这些内容的时候,要确保结构清晰,分步骤或分模块讲解,从请求处理开始,到代码解析、执行,再到资源释放输出结果。同时,避免使用过于专业的术语,或者必要时进行解释,让用户容易理解。需要检查是否有遗漏的关键点,比如安全方面的处理,或者与其他语言如Python、Ruby在运行机制上的对比,不过用户的问题可能不需要这些扩展内容,保持专注在PHP本身。</think>PHP运行机制原理可以拆解为以下几个核心步骤,结合其底层架构进行说明: ### 一、请求处理阶段 1. **Web服务器接收请求** - 当用户发起HTTP请求(如访问`.php`文件)时,Apache/Nginx等服务器通过以下方式处理: - **Apache**:通过`mod_php`模块直接调用PHP解释器 - **Nginx**:通过`FastCGI`协议与`PHP-FPM`进程通信 2. **初始化PHP环境** - 启动`Zend引擎`,加载`php.ini`配置 - 注册预定义常量(如`$_GET`、`$_POST`等超全局变量) ### 二、代码解析与执行 1. **词法分析 & 语法分析** ```php <?php echo "Hello, $name"; ?> ``` - 通过`Re2c`生成词法分析器,将代码拆解为Token序列 - `Bison`生成语法分析器,构建抽象语法树(AST) 2. **生成Opcode** - Zend引擎将AST转换为中间代码(Opcode) - 示例操作码: ``` ZEND_ECHO # 输出指令 ZEND_CONCAT # 字符串拼接 ``` 3. **执行阶段** - Zend虚拟机逐条执行Opcode - 内存管理: - 引用计数跟踪变量 - 循环引用通过垃圾回收(GC)处理 ### 三、扩展机制 1. **模块化架构** - 核心扩展(如`Standard`、`PDO`) - 第三方扩展(如`Redis`、`GD`) - 加载方式: ```ini ; php.ini配置 extension=redis.so ``` 2. **Zend API** - 扩展通过`PHP_MINIT_FUNCTION()`等钩子函数注册功能 - 示例扩展开发接口: ```c PHP_FUNCTION(my_custom_function) { // C语言实现逻辑 } ``` ### 四、生命周期管理 1. **单进程模式** - 典型流程: ``` MINIT -> RINIT -> Execute -> RSHUTDOWN -> MSHUTDOWN ``` - `MINIT`:模块初始化(仅一次) - `RINIT`:请求初始化(每次请求) 2. **PHP-FPM进程管理** - 常驻内存的Master-Worker模型 - 连接池管理: ```bash # 配置文件 pm = dynamic pm.max_children = 50 ``` ### 五、性能优化技术 1. **Opcache加速** - 缓存编译后的Opcode - 配置示例: ```ini opcache.enable=1 opcache.memory_consumption=128 ``` 2. **JIT编译(PHP 8+)** - 将Opcode直接编译为机器码 - 启用方式: ```ini opcache.jit_buffer_size=100M opcache.jit=tracing ``` ### 六、输出处理 1. 结果缓冲区管理 - 通过`ob_start()`开启多级缓冲 - 输出流经过`Content-Type`检测后发送给客户端 ### 关键特性总结 | 特性 | 说明 | |---------------------|-----------------------------| | 弱类型实现 | ZVAL结构体动态管理变量类型 | | 单请求隔离 | 每个请求独立上下文,避免内存泄漏 | | 连接持久化 | 数据库连接可通过`pconnect`复用 | 通过这种机制,PHP实现了高效的Web请求处理,同时保持开发便捷性。新的版本(如PHP8.3)通过JIT编译属性注解等改进,进一步提升了复杂应用的执行效率。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值