这篇文章写了快10年了,但是感觉很经典,收藏下来。

  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,在效率上多少会有一些优势。 
   虽然如此,PHP的开发人员仍然在努力构建PHP的统一的数据库访问接口。从PHP 5.1开始,PHP的发行包内置了PDO(PHP Data Objects,PHP数据对象)。PDO具有如下特性: 
   
   统一的数据库访问接口。PDO为访问不同的数据库提供了统一的接口,并且能够通过切换数据库驱动程序,方便地支持各种流行的数据库。 
   面向对象。PDO完全基于PHP 5的对象机制,因此区别于基于过程的专用API。 
   高性能。PDO的底层用C编写,比起用纯PHP开发的其他类似解决方案,有更高的性能。 
   
  一个典型的PDO应用如下例: 
   
  $pdo = new PDO("mysql:host=localhost;dbname=justtest", " mysql_user ", " mysql_password"); 
  $query = "SELECT id, username FROM userinfo ORDER BY ID"; 
  foreach ($pdo->query($query) as $row) { 
   echo $row['id']." | ".$row['username']."<br/>"; 
  } 
   
  但是,PDO还有一个更重要的问题没有解决,那就是对数据集的抽象。 
  无论是ADO还是JDBC,除了提供统一的数据库访问接口以外,也提供了对数据集的抽象。也就是说,在通过ADO/JDBC取回数据集结果以后,这些数据集以统一的格式被存放在RecordSet/RowSet对象中,业务逻辑代码只需要与数据集对象进行交互即可。对数据集进行抽象的直接后果,是彻底地分离了业务逻辑层和数据库访问层的代码,并且也在某种程度上起到了OR Mapping的效果。 
  自从ADO.NET出现后,数据集的抽象又有了一次不小的进步。和ADO相比,ADO.NET中的DataTable/DataSet类的主要新特性如下: 
   
   非连接性。在传统的ADO模型中,数据集需要占用一个数据库连接,直到所有工作完成。一旦连接被关闭,数据集的内容也就失效了。ADO.NET中的数据集是非连接的,也就是说,当连接被关闭后,数据集中的内容仍然保存。这种非连接性带来的直接后果是,数据库连接可以被最大限度地利用,因为一旦工作完成就可以将连接返回到数据库连接池中。(ADO也支持非连接的数据集,但是需要程序员自己实现,而ADO.NET的数据集在本质上就是非连接的。) 
   自描述性。ADO.NET中的数据集是完全自我描述的,而且具有完备的metadata,其内容不但可以从任何特定的数据库生成,而且可以由代码动态生成。DataSet可以跟踪数据的变化,并完成相应的操作。 
   互操作性。由于非连接性和自描述性,ADO.NET中的数据集可以非常方便地在网络之间进行传输。DataSet可以序列化/反序列化为XML或其他特定的格式。这样,DataSet不但可以用于同一平台的分布式网络环境,而且可以用于异构网络环境。 
   
  Java从J2SE 5.0开始内置了CachedRowSet,其原理和ADO.NET的数据集类似。Borland开发的用于取代BDE的新一代数据库引擎dbExpress,其改进也与此类似。 
  与之对比,PHP中对数据集的支持显得非常原始。无论是传统的API还是PDO,取回的数据仅仅表现为数组,并且没有任何缓存机制。这意味着,在所有需要访问数据集的地方,都必须频繁地使用直接访问数据库的API。下面是一个使用mysql API的例子: 
   
  $link = mysql_connect('localhost', 'mysql_user', 'mysql_password'); 
  if (!$link) { 
   die('Could not connect: ' . mysql_error()); 
  } 
  mysql_select_db("justtest"); 
  $result = mysql_query("SELECT id, username FROM userinfo ORDER BY ID"); 
  while ($row = mysql_fetch_array($result)) { 
   echo $row['id']." | ".$row['username']."<br/>"; 
  } 
  mysql_close($link); 
   
   这样做的后果是业务逻辑和访问数据库的代码无法分离,在规模较大的系统中尤其严重。 
   就作者所知,PHP官方没有提供支持抽象数据集的计划。但是,自己实现这样一个数据集并不是一件难事。作者参照ADO.NET的架构,使用纯PHP代码编写了一个规模非常小的数据抽象类库,姑且称之为MyPDO。MyPDO大约有1300行代码,在几个真实项目中工作得很好。由于篇幅所限,本文不列出它的所有代码,仅仅给出几个最主要的类的描述: 
   
   DataAdapter接口:定义了所有与数据库操作相关的方法。 
   ConceptDataAdapter类:实现了DataAdapter,封装了访问特定数据库的代码。如MySqlDataAdapter。 
   DataSet类:实现了自描述的抽象数据集,与具体数据库无关。可以自我跟踪数据的变化。 
   SqlCommandBuilder类:可以跟据DataSet类的内容,自动实现Insert、Update、Delete等操作。 
   
  下面是MyPDO的一个典型应用: 
   
   $Conn = new MySqlDataConnection(new ConnectionInfo("localhost", "mysql_username", "mysql_password", "justtest", "gbk")); 
   $Da = $Conn->GetDataAdapter(); 
   $Da->SetSqlString("SELECT id, username FROM userinfo ORDER BY ID"); 
   $Ds = new DataSet(); 
   $Da->Fill($Ds);
   $Conn->Disconnect(); // 关闭数据库连接,但$Ds仍然保存数据内容 
   echo $Ds; // 调用DataSet的__tostring()方法,格式化输出内容 
   
  通过替换MySqlDataConnection,可以以最小的成本实现不同数据库之间的切换。 
  由于上文中讨论过的PHP的工作模型,通过MyPDO实现的缓存在性能上获得的好处有限。但是,在采用Memcached的解决方案中,MyPDO还是能够带来很大的便利。因为只有基于非连接方式的数据集,才可能在Memcached这样的内存池中被缓存。 
  另外,由于MyPDO中的DataSet是自描述的,内置了WriteToXml和ReadFromXml方式,它无需程序员编码就可以保存为XML或从XML中还原,在网络上甚至异构平台之间进行传输和识别。在电子商务领域中,这个特性是非常有用的。 
  

PHP沉思录(转载)相关推荐

  1. PHP沉思录-第三篇-Smarty-左轻侯-《程序员》2007年10月号

    为什么80%的码农都做不了架构师?>>>    创建时间:2007-10-31 21:23:11   最后修改时间:2007-10-31 21:23:11 PHP沉思录之三:Smar ...

  2. 软件开发沉思录--ThoughtWorks文集

    软件开发沉思录--ThoughtWorks文集 市场价 :¥39.00 会员价 : ¥29.25(75折) [原出版社] Pragmatic Bookshelf  [作 者]ThoughtWorks公 ...

  3. 《C++沉思录 第2版》

    内容简介: <C和C++经典著作•C++沉思录:Ruminations on C++>基于作者在知名技术杂志发表的技术文章.世界各地发表的演讲以及斯坦福大学的课程讲义整理.写作而成,融聚了 ...

  4. 读书笔记∣概率论沉思录 01

    概率的解释基础分为两种,一是物理世界本身存在的随机性(客观概率),二是是我们由于信息不足而对事件发生可能性的度量(主观概率).基于此,形成了概率论的两大学派:频率论学派(传统数理统计学)和贝叶斯统计学 ...

  5. 《软件开发沉思录》之对象健身操

    2019独角兽企业重金招聘Python工程师标准>>> 最近看了一部分<软件开发沉思录>, 感觉里面的"对象健康操"还是蛮有意思的,在这记录下,希望以 ...

  6. 生活沉思录 via 哲理小故事(一)

    1.小托蒂的悲剧 意大利小男孩托蒂,有一只十分奇怪的眼睛,因为从生理上看,这是一只完全正常的眼睛,但却是失明的. 原来,托蒂刚出生时,这只眼睛轻度感染,曾用绷带缠了两个星期.这对常人来说几乎没有人任何 ...

  7. 程序员的奋斗史(二十)——沉思录(一)——关于博客和思想

    写在前面:如果你看到这篇文章,那是缘分.如果你因为这篇文章有所收获,那是我莫大的欣慰.请读者静下心来,认真看完本文,阅毕肯定会有不一样的感受. 写这篇博客之前看看我在知乎上提的问题和有缘人对这个问题的 ...

  8. 博客问题[置顶] 程序员的奋斗史(二十)——沉思录(一)——关于博客和思想...

    在本篇文章中,我们重要介绍博客问题的内容,自我感到有个不错的建议和大家分享下 写在前面:如果你看到这篇文章,那是缘分.如果你因为这篇文章有所收成,那是我莫大的快慰.请读者静下心来,认真看完本文,阅毕肯 ...

  9. 《沉思录》--[古罗马]马可·奥勒留

    <沉思录>,是由古罗马作家:马可·奥勒留所著. 下面是我的书摘: * 总之,在斯多亚派哲学的眼里,宇宙是一个井然有序的宇宙,世界是一个浑然和谐的世界. * 理性动物是彼此为了对方而存在的, ...

  10. 迪克森沉思录之做Global SAP项目的弊端

    迪克森沉思录之做Global SAP项目的弊端 笔者在过去的十年里,参与过好几个大型跨国企业在国内工厂的SAP推广实施项目,这种项目就是将总部设计好的全球模板推广到其海外工厂,使得其诸多海外工厂能按照 ...

最新文章

  1. HTTP长连接和短连接以及推送服务原理(转)
  2. 度量相似性的一些指标/函数
  3. keepalived安装与配置_面试官问LVS+keepalived+nginx怎么实现时该怎么答?
  4. 从 Spark 的 DataFrame 中取出具体某一行详解
  5. Apache连接和访问控制
  6. Java如何连接openvas_gas: chinese Gui for openvAS(GAS)
  7. Linux的简介与虚拟机的管理
  8. winform checkedlistbox不显示复选框_据说90%的人都不知道怎么解决这个Word问题
  9. 【答辩问题】计算机专业本科毕业设计答辩问题
  10. cmake使用教(二) install的使用
  11. iOS----创建静态库
  12. cad通过钢筋大样生成钢筋明细表插件_为什么自动生成的钢筋明细表为空 - 应用技巧 - 常青藤软件工作室...
  13. 2017 matlab 仿真,Matlab 2017a 安装程序
  14. 飞鸽传书——空号检测
  15. Java setlocale方法_Java Configuration.setLocale方法代碼示例
  16. 主板USB接口全部失效解决方案(通用串行总线USB控制器有黄色叹号)保姆级教程亲测有效
  17. 分析社会热点与网络营销的关联
  18. C#基于Socket的局域网即时通信和传输文件程序
  19. python中plot线条的形状、标记符号和颜色详情
  20. 服务器cpu和磁盘规律毛刺问题排查

热门文章

  1. ueditor接入秀米编辑器
  2. 高等数学第七版同济大学课后习题讲解上下册
  3. c语言结构体定义常量,C语言结构体
  4. Cocos2d-x组件的创建与挂载
  5. 退役前的做题记录3.0
  6. 短链接生成接口、长链接转换短链接,可根据ip归属地个性化跳转、随机跳转
  7. 00 VirtualBox安装Windows7 32位系统
  8. 学术论文的格式要求是怎样的?
  9. 麦克斯韦电磁场理论基础
  10. Power Apps一个实际案例 -1 - 需求分析