PHP运行模式 概述

PHP运行模式有4钟:

1)cgi 通用网关接口(Common Gateway Interface))

2) fast-cgi 常驻 (long-live) 型的 CGI

3) cli  命令行运行   (Command Line Interface)\n

4)web模块模式 (apache等web服务器运行的模块模式)

1、cgi (Common Gateway Interface)

CGI即通用网关接口(Common Gateway Interface),它是一段程序, 通俗的讲CGI就象是一座桥,把网页和WEB服务器中的执行程序连接起来,它把HTML接收的指令传递给服务器的执行程序,再把服务器执行程序的结果返还给HTML页。CGI 的跨平台性能极佳,几乎可以在任何操作系统上实现。 CGI已经是比较老的模式了,这几年都很少用了。

每有一个用户请求,都会先要创建cgi的子进程,然后处理请求,处理完后结束这个子进程,这就是fork-and-execute模式。 当用户请求数量非常多时,会大量挤占系统的资源如内存,CPU时间等,造成效能低下。所以用cgi方式的服务器有多少连接请求就会有多少cgi子进程,子进程反复加载是cgi性能低下的主要原因。

如果不想把 PHP 嵌入到服务器端软件(如 Apache)作为一个模块安装的话,可以选择以 CGI 的模式安装。或者把 PHP 用于不同的 CGI 封装以便为代码创建安全的 chroot 和 setuid 环境。这样每个客户机请求一个php文件,Web服务器就调用php.exe(win下是php.exe,linux是php)去解释这个文件,然后再把解释的结果以网页的形式返回给客户机。 这种安装方式通常会把 PHP 的可执行文件安装到 web 服务器的 cgi-bin 目录。

建议不要把任何的解释器放到 cgi-bin 目录。 这种方式的好处是把web server和具体的程序处理独立开来,结构清晰,可控性强,同时缺点就是如果在高访问需求的情况下,cgi的进程fork就会成为很大的服务器负担,想 象一下数百个并发请求导致服务器fork出数百个进程就明白了。这也是为什么cgi一直背负性能低下,高资源消耗的恶名的原因。

CGI已经是比较老的模式了,这几年都很少用了,所以我们只是为了测试。

2、fast-cgi模式

fast-cgi 是cgi的升级版本,FastCGI 像是一个常驻 (long-live) 型的 CGI,它可以一直执行着,只要激活后,不会每次都要花费时间去 fork 一次 (这是 CGI 最为人诟病的 fork-and-execute 模式)。\n FastCGI的工作原理是

  1. Web Server启动时载入FastCGI进程管理器【PHP的FastCGI进程管理器是PHP-FPM(php-FastCGI Process Manager)】(IIS ISAPI或Apache Module);
  2. FastCGI进程管理器自身初始化,启动多个CGI解释器进程 (在任务管理器中可见多个php-cgi.exe)并等待来自Web Server的连接。
  3. 当客户端请求到达Web Server时,FastCGI进程管理器选择并连接到一个CGI解释器。Web server将CGI环境变量和标准输入发送到FastCGI子进程php-cgi。
  4. FastCGI子进程完成处理后将标准输出和错误信息从同一连接返回Web Server。当FastCGI子进程关闭连接时,请求便告处理完成。FastCGI子进程接着等待并处理来自FastCGI进程管理器(运行在 WebServer中)的下一个连接。在正常的CGI模式中,php-cgi.exe在此便退出了。

在CGI模式中,你可以想象 CGI通常有多慢。每一个Web请求PHP都必须重新解析php.ini、重新载入全部dll扩展并重初始化全部数据结构。使用FastCGI,所有这些都只在进程启动时发生一次。一个额外的好处是,持续数据库连接(Persistent database connection)可以工作。

Fastcgi的优点:

 1)从稳定性上看, fastcgi是以独立的进程池运行来cgi,单独一个进程死掉,系统可以很轻易的丢弃,然后重新分 配新的进程来运行逻辑.

2)从安全性上看,Fastcgi支持分布式运算. fastcgi和宿主的server完全独立, fastcgi怎么down也不会把server搞垮.

3)从性能上看, fastcgi把动态逻辑的处理从server中分离出来, 大负荷的IO处理还是留给宿主server, 这样宿主server可以一心一意作IO,对于一个普通的动态网页来说, 逻辑处理可能只有一小部分, 大量的图片等静态 

FastCGI缺点

说完了好处,也来说说缺点。从我的实际使用来看,用FastCGI模式更适合生产环境的服务器。但对于开发用机器来说就不太合适。因为当使用 Zend Studio调试程序时,由于 FastCGI会认为 PHP进程超时,从而在页面返回 500错误。这一点让人非常恼火,所以我在开发机器上还是换回了 ISAPI模式。

3、cli模式

cli是php的命令行运行模式,大家经常会使用它,但是可能并没有注意到(例如:我们在linux下经常使用 \”php -m\”查找PHP安装了那些扩展就是PHP命令行运行模式;有兴趣的同学可以输入php -h去深入研究该运行模式)

4、模块模式

模块模式是以mod_php5模块的形式集成,此时mod_php5模块的作用是接收Apache传递过来的PHP文件请求,并处理这些请求,然后将处理后的结果返回给Apache。如果我们在Apache启动前在其配置文件中配置好了PHP模块(mod_php5), PHP模块通过注册apache2的ap_hook_post_config挂钩,在Apache启动的时候启动此模块以接受PHP文件的请求。

除了这种启动时的加载方式,Apache的模块可以在运行的时候动态装载,这意味着对服务器可以进行功能扩展而不需要重新对源代码进行编译,甚至根本不需要停止服务器。我们所需要做的仅仅是给服务器发送信号HUP或者AP_SIG_GRACEFUL通知服务器重新载入模块。但是在动态加载之前,我们需要将模块编译成为动态链接库。此时的动态加载就是加载动态链接库。 Apache中对动态链接库的处理是通过模块mod_so来完成的,因此mod_so模块不能被动态加载,它只能被静态编译进Apache的核心。这意味着它是随着Apache一起启动的。

Apache是如何加载模块的呢?我们以前面提到的mod_php5模块为例。首先我们需要在Apache的配置文件httpd.conf中添加一行:

该运行模式是我们以前在windows环境下使用apache服务器经常使用的,而在模块化(DLL)中,PHP是与Web服务器一起启动并运行的。(是apache在CGI的基础上进行的一种扩展,加快PHP的运行效率)

LoadModule php5_module modules/mod_php5.so  这里我们使用了LoadModule命令,该命令的第一个参数是模块的名称,名称可以在模块实现的源码中找到。第二个选项是该模块所处的路径。如果需要在服务器运行时加载模块,可以通过发送信号HUP或者AP_SIG_GRACEFUL给服务器,一旦接受到该信号,Apache将重新装载模块,而不需要重新启动服务器。

5、php 在nginx 中运行模式(nginx+PHP-FPM )

使用FastCGI方式现在常见的有两种stack:ligthttpd+spawn-fcgi;另外一种是nginx+PHP-FPM(也可以用spawn-fcgi)。\n \n A、如上面所说该两种结构都采用FastCGI对PHP支持,因此HTTPServer完全解放出来,可以更好地进行响应和并发处理。因此lighttpd和nginx都有small, but powerful和efficient的美誉。\n \n B、该两者还可以分出一个好坏来,spawn-fcgi由于是lighttpd的一部分,因此安装了lighttpd一般就会使用spawn-fcgi对php支持,但是目前有用户说ligttpd的spwan-fcgi在高并发访问的时候,会出现上面说的内存泄漏甚至自动重启fastcgi。即:PHP脚本处理器当机,这个时候如果用户访问的话,可能就会出现白页(即PHP不能被解析或者出错)。\n \n 另一个:首先nginx不像lighttpd本身含带了fastcgi(spawn-fcgi),因此它完全是轻量级的,必须借助第三方的FastCGI处理器才可以对PHP进行解析,因此其实这样看来nginx是非常灵活的,它可以和任何第三方提供解析的处理器实现连接从而实现对PHP的解析(在nginx.conf中很容易设置)。nginx可以使用spwan-fcgi(需要一同安装lighttpd,但是需要为nginx避开端口,一些较早的blog有这方面安装的教程),但是由于spawn-fcgi具有上面所述的用户逐渐发现的缺陷,现在慢慢减少使用nginx+spawn-fcgi组合了。\n \n\n \n 由于它是作为PHP的patch补丁来开发的,安装的时候需要和php源码一起编译,也就是说编译到php core中了,因此在性能方面要优秀一些;\n \n\n \n 因此,如上所说由于nginx的轻量和灵活性,因此目前性能优越,越来越多人逐渐使用这个组合:nginx+PHP/PHP-FPM\n 

总结:

目前在HTTPServer这块基本可以看到有三种\n stack\n 比较流行:

(1)Apache+mod_php5

(2)lighttp+spawn-fcgi

(3)nginx+PHP-FPM

三者后两者性能可能稍优,但是\n Apache\n 由于有丰富的模块和功能,目前来说仍旧是老大。有人测试\n nginx+PHP-FPM\n 在高并发情况下可能会达到Apache+mod_php5\n 的\n 5~10\n 倍,现在\n nginx+PHP-FPM\n 使用的人越来越多。

本文由来源 21aspnet

抽象工厂设计模式-创建型设计模式 [PHP]

抽象工厂:\n在不指定具体类的情况下创建一系列相关或从属对象。最重要的特征就是抽象类的组合.\n通常,创建的类都实现相同的接口。抽象工厂的客户端并不关心这些对象的创建方式,只是知道它们如何组合在一起。

三个抽象类

 

interface WriterFactory{\n    public function createCsvWriter(): CsvWriter;\n    public function createJsonWriter(): JsonWriter;\n}\ninterface CsvWriter{\n    public function write(array $line): string;\n}\ninterface JsonWriter{\n    public function write(array $data, bool $formatted): string;\n}

分为linux下和win下的六个具体实现类:\nUnixWriterFactory WinWriterFactory 这俩就是传说中的抽象工厂, 生成下面的抽象类的具体实例\nUnixCsvWriter WinCsvWriter\nUnixJsonWriter WinJsonWriter

具体使用:

具体使用:\nclass AbstractFactoryTest extends TestCase{\n    //提供工厂,提供了两个系统下的工厂\n    public function provideFactory()\n    {\n        return [\n            [new UnixWriterFactory()],\n            [new WinWriterFactory()]\n        ];\n    }\n    //使用提供的工厂,创建对象,当我们在定义参数类型时都可以直接使用抽象类的名字来定义\n    public function testCanCreateCsvWriterOnUnix(WriterFactory $writerFactory)\n    {\n        $this->assertInstanceOf(JsonWriter::class, $writerFactory->createJsonWriter());\n        $this->assertInstanceOf(CsvWriter::class, $writerFactory->createCsvWriter());\n    }\n}

Android App端与PHP Web端的简单数据交互实现示例

需要实现Android App端与PHP Web端的简单数据交互的实现,当前场景是Web端使用的是MySql数据库,Apache服务器和PHP语言编写的。数据交互的简单理解就是Android能向服务端进行数据获取,同时也能进行数据提交。

实现流程

流程说明

  1. Andorid Server端对MySql数据库进行简单的查询操作,并将查询数据结果转换为Json格式提供给Andorid利用OKhttp读取再解析Json展示到APP上;同时Andorid端利用OKhttp提交给Andorid Server端,由Server端对MySql数据库对提交数据的添加。
  2. Apache Server端通过解析PHP源代码,对MySql数据库的增删查改显示在WebSite。

具体实现

Andorid Server

获取数据

get_all_found_items.php

<?php \nheader(\'Content-Type:text/html;charset=utf-8\');/*设置php编码为utf-8*/\n/* \n * Following code will list all the items \n */ \n  \n// array for JSON response \n$response = array(); \n  \n// include db connect class \nrequire_once __DIR__ . \'/db_connect.php\'; \n  \n// connecting to db \n$db = new DB_CONNECT(); \n  \n// get all items from items table \n$result = mysql_query(\"SELECT *FROM items WHERE type=\'1\'\") or die(mysql_error()); \n// check for empty result \nif (mysql_num_rows($result)   0) { \n  // looping through all results \n  // items node \n  $response[\"items\"] = array(); \n  \n  while ($row = mysql_fetch_array($result)) { \n    // temp user array \n    $items = array(); \n    $items[\"what\"] = $row[\"what\"]; \n    $items[\"when\"] = $row[\"when\"]; \n    $items[\"where\"] = $row[\"where\"]; \n    $items[\"detail\"] = $row[\"detail\"];\n    $items[\"posttime\"] = $row[\"posttime\"];  \n $resultcontcat = mysql_query(\"SELECT *FROM guests\") or die(mysql_error()); \n while ($row1 = mysql_fetch_array($resultcontcat)) { \n  if ($row1[\"id\"] == $row[\"gid\"]){\n  $items[\"contact\"] = $row1[\"contact\"];\n  }\n  }\n    // push single items into final response array \n    array_push($response[\"items\"], $items); \n  } \n  // success \n  $response[\"success\"] = 1; \n  \n  // echoing JSON response \n  echo json_encode($response,JSON_UNESCAPED_UNICODE); \n} else { \n  // no items found \n  $response[\"success\"] = 0; \n  $response[\"message\"] = \"No items found\"; \n  \n  // echo JSON \n  echo json_encode($response,JSON_UNESCAPED_UNICODE); \n} \n? 

如以上PHP代码可知通过require_once()函数包含db_connect.php文件,执行数据库配置文件。定义数组$response接收查询的数据结果,通过判断不同的情况赋值$response[“success”],并返回到Web页面显示

PHP文件执行结果JSON

 {\n  \"items\": [\n    {\n      \"what\": \"手表\",\n      \"when\": \"2017-10-21 00:00:00\",\n      \"where\": \"北区宿舍楼#504\",\n      \"detail\": \"白色的手表,XX品牌\",\n      \"posttime\": \"2017-10-21 13:03:09\",\n      \"contact\": \"138123456\"\n    },\n    {\n      \"what\": \"手机\",\n      \"when\": \"2017-10-04 00:00:00\",\n      \"where\": \"北区商店#111\",\n      \"detail\": \"iphone6s,土豪金\",\n      \"posttime\": \"2017-10-21 13:03:46\",\n      \"contact\": \"137123456\"\n    },\n    {\n      \"what\": \"电脑\",\n      \"when\": \"2017-10-21 14:39:54\",\n      \"where\": \"图书馆#203\",\n      \"detail\": \"联想品牌笔记本\",\n      \"posttime\": \"2017-10-21 17:08:14\",\n      \"contact\": \"5670001\"\n    },\n    {\n      \"what\": \"细说PHP\",\n      \"when\": \"2017-09-21 13:03:46\",\n      \"where\": \"南馆#403\",\n      \"detail\": \"黑色封面,第二版《细说PHP》\",\n      \"posttime\": \"2017-10-21 17:36:53\",\n      \"contact\": \"63513641\"\n    }\n  ],\n  \"success\": 1\n}

提交数据

create_found_items.php

<?php \nheader(\'Content-Type:text/html;charset=utf-8\');/*设置php编码为utf-8*/  \n/* \n * Following code will create a new product row \n * All product details are read from HTTP GET Request \n */ \n  \n// array for JSON response \n$response = array(); \n  \n// check for required fields \nif (isset($_GET[\'what\']) && isset($_GET[\'when\']) && isset($_GET[\'where\']) && isset($_GET[\'detail\'])&& isset($_GET[\'contact\'])) { \n  \n  $what = $_GET[\'what\']; \n  $when = $_GET[\'when\']; \n  $where = $_GET[\'where\']; \n  $detail = $_GET[\'detail\']; \n  $contact = $_GET[\'contact\']; \n \n  // include db connect class \n  require_once __DIR__ . \'/db_connect.php\'; \n  \n  // connecting to db \n  $db = new DB_CONNECT(); \n  \n  // mysql inserting a new row \n $result2 = mysql_query(\"INSERT INTO guests(contact) VALUES(\'$contact\')\"); \n $gidresult = mysql_query(\"SELECT id FROM guests WHERE contact=\'$contact\'\"); \n while ($row = mysql_fetch_array($gidresult)) { \n $gid=$row[\'id\'];\n }\n $result1 = mysql_query(\"INSERT INTO items(what, when, where, type ,gid, detail) VALUES(\'$what\', \'$when\', \'$where\', \'1\', \'$gid\', \'$detail\')\"); \n \n // check if row inserted or not \n if ($result1 && $result2) { \n // successfully inserted into database \n $response[\"success\"] = 1; \n $response[\"message\"] = \"Items successfully created.\"; \n \n // echoing JSON response \n echo json_encode($response,JSON_UNESCAPED_UNICODE); \n } else { \n // failed to insert row \n $response[\"success\"] = 0; \n $response[\"message\"] = \"Oops! An error occurred.\"; \n \n // echoing JSON response \n echo json_encode($response,JSON_UNESCAPED_UNICODE); \n } \n} else { \n // required field is missing \n $response[\"success\"] = 0; \n $response[\"message\"] = \"Required field(s) is missing\"; \n \n // echoing JSON response \n echo json_encode($response,JSON_UNESCAPED_UNICODE); \n} \n? 

判断GET请求的参数是否都存在,把获取的GET请求参数作为数据INSERT TO MySQL数据库。判断INSERT执行过程赋值$response[“success”]对应相应的$response[“message”],显示在Web页面。

Andorid

获取数据核心代码 queryLosts()函数

private void queryLosts() {\n losts.clear();\n new Thread(new Runnable() {\n \n @Override\n public void run() {\n  // TODO Auto-generated method stub\n  \n     OkHttpClient okHttpClient = new OkHttpClient();\n     String url = \"http://webSite/androidapi/get_all_lost_items.php\";\n     Request request = new Request.Builder()\n         .url(url)\n         .build();\n     Call call = okHttpClient.newCall(request);\n     try {\n       Response response = call.execute();\n       String res = response.body().string();\n       if (res != null && !res.trim().equals(\"\")){\n         JSONObject jsonObject = new JSONObject(res);\n         if (jsonObject.getInt(\"success\") == 1){\n           JSONArray jsonArray = jsonObject.getJSONArray(\"items\");\n           for (int i = jsonArray.length() - 1;i  = 0;i--){\n             JSONObject item = jsonArray.getJSONObject(i);\n             String what = null;\n             try {\n               what = item.getString(\"what\");\n             }catch (Exception e){\n             }\n             String when = null;\n             try{\n               when = item.getString(\"when\");\n             }catch (Exception e){\n             }\n             String where = null;\n             try{\n               where = item.getString(\"where\");\n             }catch (Exception e){\n             }\n             String detail = null;\n             try {\n               detail = item.getString(\"detail\");\n             }catch (Exception e){\n             }\n             String contact = null;\n             try {\n               contact = item.getString(\"contact\");\n             }catch (Exception e){\n             }\n             Lost lost = new Lost();\n             lost.setTitle(what);\n             String des = \"地点:\" + (where == null?\"\":where) +\"   \"+\"时间:\" + (when == null?\"\":when)+\"r\" + \"   \"+\"描述:\" + (detail == null?\"\":detail);\n             lost.setDescribe(des);\n             lost.setPhone(contact == null?\"\":contact);\n             losts.add(lost);\n           }\n         }\n       }\n     } catch (Exception e) {\n       e.printStackTrace();\n       showErrorView(0);\n     }\n     if (losts == null || losts.size() == 0) {\n     handler.sendEmptyMessage(1);\n       return;\n     }\n     if (losts.size()   0){\n      handler.sendEmptyMessage(2);\n     }\n }\n }).start();

利用Android网络框架OKhttp,OKhttp一个处理网络请求的开源项目,是安卓端最火热的轻量级框架.请求接口url地址,获取Json数据利用JSONObject对Json数据进行解析。

提交数据核心代码 addLost()函数

public Handler handler = new Handler(){\n public void handleMessage(android.os.Message msg) {\n switch(msg.what){\n  case 1:\n  Toast.makeText(this,\"提交成功\",Toast.LENGTH_LONG).show();\n  break;\n  case 2:\n  Toast.makeText(this,\"提交失败\",Toast.LENGTH_LONG).show();\n  break;\n }\n }\n};\nprivate void addLost(){\n OkHttpClient okHttpClient = new OkHttpClient();\n String url =\"http://website/androidapi/create_lost_items.php?what=\"+title+\"&when=\"+time+\"&where=\"+place+\"&detail=\"+describe+\"&contact=\"+photo+\"\";\n Request request = new Request.Builder()\n  .url(url)\n  .build();\n \n try{\n Response response = okHttpClient.newCall(request).execute();\n res = response.body().string();\n handler.sendEmptyMessage(1);\n }catch (Exception e)\n {\n e.printStackTrace();\n handler.sendEmptyMessage(2);\n }\n}

同样利用Okhttp,GET方式提交参数,try-catch获取异常,通过返回值给出一定的提交结果提示。