《写给PHP开发者的Node.js学习指南》一2.2 预定义的PHP变量

简介:

本节书摘来自异步社区《写给PHP开发者的Node.js学习指南》一书中的第2章,第2.1节,作者【美】Daniel Howard,更多章节内容可以访问云栖社区“异步社区”公众号查看

2.2 预定义的PHP变量

写给PHP开发者的Node.js学习指南
当一个支持PHP的Web服务器执行一个PHP页面时,它并不是仅提供一个未处理的对某个页面的HTTP request,然后执行这个页面。如果它这样做的话,那么每一个PHP页面都需要大量额外的代码来解析原始的HTTP request并且把这些值用更方便的方式存储起来。相反,PHP引擎解码原始的HTTP请求,并将数据填充到一堆众所周知的PHP全局变量中。这些全局变量被正确填充才能保证PHP页面正常工作。

由于我们采用的基本方法是将PHP页面拷贝到本地模块中并将其转换成Node.js代码,那么我们需要自己在Node.js中实现这些全局变量以保证转换过的页面能正常工作。通过分析PHP页面,我们可以决定它依赖于哪些变量。并不是每一个PHP引擎提供的全局变量都需要实现。相反,我们仅实现那些被使用的到的变量。

有五个PHP预定义的全局变量是最常用的:$_GET、$_POST、$_COOKIE、$_REQUEST和$_SESSION。

一个HTTP request被发送时总是有一个HTTP操作,它被叫作方法或者动词。一个HTTP GET操作是非常简单的:客户端向服务器请求获取一个页面。当用户在浏览器的地址栏里键入URL时,他就是在输入一个HTTP GET request。

HTTP GET request可能有一些以名称/值对形式的参数。这些参数通常叫做查询参数或者查询字符串。用户可以手动向浏览器的地址栏中的URL最后添加添加一个问号(?)并将名称/值对以&符号分割添加到后面。键值对本身之间以等号分割。这有一个例子:


cef5de5678396d938c4e9dc8af6eedf154f434e2

在这个例子中的键值对有:theme=green、tab=users和fastload=true。当一个PHP页面获取到一个像这个例子中一样的GET request时,PHP引擎从原始HTTP GET request中提取出这些键值对并把它们放到预定义的PHP$_GET数组。名字作为$_GET数组中的键值或索引,值就是值。对于之前的URL例子,$_GET数组看起来就像这样:


985357c2168c9c4b09f6d6fa2443d9162488105c

当PHP页面被转换成Node.js代码后,Node.js仍然需要这些预定义的数组存在并被正确填充。接下来的代码展示了一个Node.js函数 initGET(),它可以被用在任何转换过的PHP页面的本地模块中用来填充一个Node.js_GET变量,这个变量就像PHP中的$_GET变量一样工作:


65c4f7a7a4f5b6558efc7687931004f1022886d2

Node.js函数initGET()需要三个参数:req、pre和cb。req参数包含一个原始的HTTP request。pre参数是一个包含了所有预定义的全局变量的Node.js对象。所有预定义的变量都存储在pre变量中,而不是一堆不同的变量,这样可以方便地传递它。而cb包含了一个在initGET()函数结束时会被调用的回调函数。由于initGET()函数仅进行了一些简单的内存操作并且无需进行有回调函数的操作,因此在技术上并不需要回调函数。但是,由于稍后将要实现的initPOST()函数将会需要一个回调函数cb()作为参数,所以最好让initGET()和initPOST()函数保持一致。

initGET()函数的第一行代码在参数pre中创建了一个名为_GET的数组。pre._GET 就是相当于PHP中$_GET数组的Node.js对象。接下来再从main URL中提取出查询参数,而这个main URL则是从req.url属性获取的。通过使用split()函数来将每一个URL查询参数分离开来进而区分它们的名字/值对使得填充pre._GET变量也非常简单。最后,调用cb参数让回调函数知道pre._GET变量已经可用。

为了初始化Node.js pre._GET变量,需要对exports.serve()函数进行一些修改。这里是最初的exports.serve()函数:


ca75b9d4128b97ddb57aa3dfa71171ab977bf97a

这里我们并不是在exports.serve()函数中实现真实的页面,而是使用一个新的函数叫作page(),exports.serve()将被保留作为初始化和完成其他PHP引擎为PHP页面做的工作:


f3512b1788dca35f656ad790126286d34cbe9a29

page()函数需要四个参数:req、res、pre和cb。req和res参数代表HTTP request和HTTP response。pre参数是预定义的变量,包含了存储查询参数的_GET属性。cb参数是一个回调函数,它可以让exports.serve()函数知道什么时候页面被完全处理完了。

将pre对象打印出来可以帮助调试。通过使用require()函数加载内建的util模块,并在res.end()函数调用中添加一个util.inspect()函数调用就可以把pre变量中包括_GET属性的所有内容显示在HTTP response中:


389d67bdc4153b00f604499e1ea10339d1e8053b

现在处理页面的操作已经移动到page()函数中了,exports.serve()函数被修改成进行初始化工作,包括调用initGET()函数:


60b2a62fd34cc8655a55c6e1de63fe432bc83c39

pre变量是最先被创建的。然后调用initGET()函数,当它完成时,则调用page()函数。在page()函数之后并没有终止化或清除操作,所以它的回调函数是空的。

当_GET属性被实现之后,就可以修改page()函数使用查询参数。修改page()函数,期待接收一个x查询参数并返回对应的值:


14da2825c4083b529477d9fde0eb5d971ca3d303

如果Node.js服务器正在运行,并且浏览器被指向到http://localhost: 1337/index.php?x=4,浏览器会显示“The value of x is 4.”。

HTTP POST request和HTTP GET request基本一样,除了键值对是通过request正文而不是URL最后的查询字符串进行发送的。一个HTTP request包括HTTP header和一个HTTP body。包含查询字符串的URL便是HTTP header中的一个。HTTP header内容精简并且有长度限制的。尤其是包含查询字符串的URL,需要限制在一定长度,不推荐使用过长的URL。相应地,当需要在HTTP request中包含很多数据时,推荐把数据放到HTTP body中作为HTTP POST的一部分。HTTP body跟被限制长度的HTTP header不一样,它可以处理非常大量的数据。对于HTTP POST,HTTP body通常被称为POST数据。

由于HTTP POST中的正文可能非常巨大,所以POST数据通常不是一次性发送的;它在收到变成可用的事件时会被发送。事件是Node.js用来指示某件事情发生的一种方法。例如,一个data事件表明下一个数据块已经从HTTP body中被读出。假如这里有很多数据,Node.js会在读取每一块数据时触发若干事件。

一个时间可以跟回调函数联系到一起,这也被叫作事件处理程序。事件处理程序会在一个事件发生时被执行。

on()函数可以将一个事件和事件处理函数联系到一起。下面的例子说明了如何使用on函数把data事件跟一个数据处理函数绑定在一起,这个数据处理函数会将数据写到控制台:


8f24e0c011d1fcfb348a5267f7db207c9255152c

对于initPOST()函数,pre的_POST属性会被初始化。就像从initGET()函数中重新调用后一样,pre参数是一个包含了所有预定义的全局变量的Node.js对象。一个body变量会被创建出来保存到被读取的HTTP body。on()函数把一个事件处理程序和data事件联系到一起,这个事件处理程序会在数据变为可用之后将其写入到body变量中:


fdad9941557266d438261c8fcb955c543b68b810

在data事件处理程序中添加的if语句是用来检测HTTP正文是否太长,如果是则会中断连接。写得不好或恶意的客户端可能会发送无限量的数据,在这时该if语句就需要放弃那个发送的大量数据的HTTP request来保护Node.js服务器。

最后,on()函数为end事件绑定一个事件处理程序,它会在整个HTTP正文被读取之后触发。通过简单的split()函数调用,end事件处理程序提取出数据并把它们放到pre._POST变量中:


530f61ef46275e24337b235a51d2257fe2a267e2

cb()在最后被调用时,已经有正确的pre._POST变量值可以使用了。

将所有代码放到一起,initPOST()函数的全文显示如下:


dd8d8748bea1420fb6e8a33bd19a15e0300b6b48

对于那些期待HTTP GET request的页面,必须修改exports.serve()函数。而对于那些期待HTTP POST request的页面,exports.serve()函数的代码则是一样的,除了调用initGET()函数的地方替换为initPOST()函数调用。设计就是这样的。尽管initGET()函数不需要一个回调函数,但是给initGET()函数一个回调函数让它和initPOST()函数有一样的参数并且代码几乎相同。


e25770a6a17fe7e33f853def90f9dd6ebf04df87

到目前为止,HTTP GET和HTTP POST是最常用的HTTP操作,并且在大多数的情况下,这也是一个Web应用程序仅需要的HTTP操作。还有一些其他的HTTP操作,如HTTP PUT、HTTP DELETE和HTTP HEAD,但是PHP引擎并没有对这些操作提供支持,所有Node.js移植通常也不需要提供支持。

cookie是一个服务器发送到客户端(通常是浏览器)的键值对,客户端会将cookie存起来并将它作为一个HTTP header添加到每一个后续的HTTP请求中。客户端通常将cookie存储在一个可持久化的地方,如硬盘中,因此能在将来的HTTP请求中使用这个cookie,即使客户端被关闭并重新启动后。cookie是服务器提供给客户端的一小段数据,它可以帮助服务器验证客户端,例如让客户端可以自动登录自己的账户。

cookie的HTTP header的名字就是“Cookie”:


a831abe2b559cfb99d5e6ea7369015b9cf9ef594

在Node.js的HTTP请求中,cookies是存储在HTTP请求的headers.cookie属性中的,在本书中的例子里就是req。

initCOOKIE()与initGET()函数都用于将cookie从相应的HTTP头中提取出来并放到pre._COOKIE变量中,非常相似。获取cookie会更方便:cookie不是附加 在URL最后作为查询字符串而是有自己的属性值。pre._COOKIE变量将会 用来替代PHP引擎为PHP页面提供的PHP $_COOKIE变量:


30ee68d65d998494c6f686bc6d07bf295300004c

就像那些期待HTTP GET和HTTP POST请求的页面,那些需要cookie的页面需要修改它们的exports.serve()函数来调用initCOOKIE()函数。initCOOKIE()函数跟initGET()和initPOST()有同样的参数,因此可以使用同样的代码调用initCOOKIE()函数:


258a1b694439a8f992db89f610511e92727fb5db

当一个页面同时需要处理HTTP GET和HTTP POST两种请求并且还需要用到cookie时,可以通过将一个函数当作另一个函数的回调函数的方法来增强exports.serve()函数初始化的操作。以下代码用于加载pre._GET、pre._POST和pre._COOKIE属性,它们将用于替代PHP预定义变量$_GET、$_POST和$_COOKIE:


e606ff666443ad80e885c581bd0757fc0c91ee2c

在PHP里,$_REQUEST预定义变量包含了在$_GET、$_POST和$_COOKIE中所有的键值对。我们需要将pre._GET、pre_POST和pre._COOKIE变量复制一份来创建 pre._REQUEST:


054738b400b769c1a6186ccd3f9e3e9cdd8dd071

Node.js中的for…in语句可以从一个Node.js对象找出所有的属性名。下面的代码显示了一个对象obj并且把它所有的属性名打印出来:


8ac7f02f88a4c8912763c1d5cd087f19e7f0b9e9

这里是Node.js的for…in循环的输出:


a2dd69ed34496aa9e38d8c600e08cd5757db6960

在PHP中,foreach…as与Node.js中的for…in工作行为差不多,除了foreach…as会返回PHP中的属性值而不是像for…in在Node.js中返回属性名。

就像其他预定义变量的初始化函数一样,initREQUEST()函数必须被exports.serve()函数调用。因为_REQUEST值是_GET、_POST和_COOKIE的一个复合体,构造_REQUEST值需要的三个值已经赋值给pre变量,因此 initREQUEST()函数必须要在其他函数初始化之后被调用:


53e813a683da2ad23ce2d62ec7d50d9568e3ae36

initREQUEST()函数就像期待的那样与其他初始化函数使用完全相同的参数并且表现也相同。

在PHP还有一个常用的预定义变量:$_SESSION变量。$_SESSION变量代表了每一个用户对 PHP页面的调用。

initSESSION()函数使用pre._COOKIE变量维护当前用户在sessions变量中的会话。当会话不存在时,它会被创建出来;否则,将从sessions对象中获取:


f6d8acae0d1d33fdaa934614801a224c8b714b20

同样也需要让页面处理会话。通过将一个函数添加到另一个函数的回调函数中来加强exports.serve()初始化函数的能力。下面的代码会加载pre._GET、pre._POST、pre._COOKIE和pre._REQUEST属性,它们是用来代替PHP中预定义的变量$_GET、$_POST、$_COOKIE和$_REQUEST的。

initSESSION()函数是在exports.serve()函数中调用的最后一个函数。它依赖于PHP变量$_COOKIE来维护这个会话。为了保证cookie处于被激活的状态,需要修改page()函数的回调函数将cookie返回给调用者:


4f7c8d0fcdd98349032d75109b2660f9e6d21d6b

就像所期待的那样,initSESSION()函数跟其他初始化函数一样采用完全相同的参数,并且执行方式也几乎相同。

这里我们创建一个本地模块initreq.njs用来将initGET()、initPOST()、initCOOKIE()、initREQUEST()和initSESSION()函数共享给所有其他模块。并且这些函数作为属性赋值给exports变量,从而使它们可以暴露给加载该模块的调用者:


80b3fdf51ac4a5b11e768403f09e35d8074657dc


92e15dde280eff33a11d2c76a42e81e36455861e

为了使用这个initreq.njs本地模块,首先需要用require()函数加载它。然后,需要将模块的名字initreq置于initreq.njs文件暴露出来的每一个初始函数的引用之前来进行调用。下面的代码显示了这些改变:


5ba09444ddab6f55adbd532b93e3b12dded0757e

现在你对于从PHP到Node.js的转换应该有更好的想法并且知道整个过程是如何工作的了。

httpsvr.njs文件是一个Node.js HTTP服务器。在一个常见的PHP设置中,httpsvr.njs文件类似于一个安装了PHP模块的Apache Web服务器。如果你想要调整Node.js Web服务器来添加一些页面,将URL指定到特定的页面或者执行其他通用的Web服务器的配置时,那么就需要修改httpsvr.njs文件。我们把之前的httpsvr.njs例子再放到这里以便于引用:


deafe2fefb144af4fc844a3e205d1fde6d081983

对于每一个PHP页面,都会有一个index.njs文件或其他模块文件被创建出来。exports.serve()是Node.js中对应于PHP引擎用来处理某个特定页面代码的函数。如果需要一些额外的预定义变量,初始化代码或者结束代码(例如,在页面完成之后执行的代码),就需要修改exports.serve()函数。exports.serve()函数并不是页面本身,而是“包裹”这个页面的代码:


43d08916a3fe04bc2ea620e33ccbba1daf046798
相关文章
|
3月前
|
JSON 自然语言处理 前端开发
【01】对APP进行语言包功能开发-APP自动识别地区ip后分配对应的语言功能复杂吗?-成熟app项目语言包功能定制开发-前端以uniapp-基于vue.js后端以laravel基于php为例项目实战-优雅草卓伊凡
【01】对APP进行语言包功能开发-APP自动识别地区ip后分配对应的语言功能复杂吗?-成熟app项目语言包功能定制开发-前端以uniapp-基于vue.js后端以laravel基于php为例项目实战-优雅草卓伊凡
187 72
【01】对APP进行语言包功能开发-APP自动识别地区ip后分配对应的语言功能复杂吗?-成熟app项目语言包功能定制开发-前端以uniapp-基于vue.js后端以laravel基于php为例项目实战-优雅草卓伊凡
|
8月前
|
存储 API PHP
|
3月前
|
存储 PHP
PHP中如何定义常量以及常量和变量的主要区别
常量和变量在PHP中扮演着不同的角色,各有各的应用场景。常量用于存储不会改变的值,具有全局作用域,定义后在整个脚本中都可以访问。变量则用于存储在程序执行过程中可能改变的值,作用域可以是局部的或全局的。掌握常量和变量的定义和区别,有助于编写出更加清晰、可维护的代码。希望本文能帮助你更好地理解和使用PHP中的常量和变量。
105 20
|
9月前
|
设计模式 数据库连接 PHP
PHP中的设计模式:提升代码的可维护性与扩展性在软件开发过程中,设计模式是开发者们经常用到的工具之一。它们提供了经过验证的解决方案,可以帮助我们解决常见的软件设计问题。本文将介绍PHP中常用的设计模式,以及如何利用这些模式来提高代码的可维护性和扩展性。我们将从基础的设计模式入手,逐步深入到更复杂的应用场景。通过实际案例分析,读者可以更好地理解如何在PHP开发中应用这些设计模式,从而写出更加高效、灵活和易于维护的代码。
本文探讨了PHP中常用的设计模式及其在实际项目中的应用。内容涵盖设计模式的基本概念、分类和具体使用场景,重点介绍了单例模式、工厂模式和观察者模式等常见模式。通过具体的代码示例,展示了如何在PHP项目中有效利用设计模式来提升代码的可维护性和扩展性。文章还讨论了设计模式的选择原则和注意事项,帮助开发者在不同情境下做出最佳决策。
|
9月前
|
设计模式 算法 数据库连接
PHP中的设计模式:提高代码的可维护性与扩展性本文旨在探讨PHP中常见的设计模式及其应用,帮助开发者编写出更加灵活、可维护和易于扩展的代码。通过深入浅出的解释和实例演示,我们将了解如何使用设计模式解决实际开发中的问题,并提升代码质量。
在软件开发过程中,设计模式是一套经过验证的解决方案模板,用于处理常见的软件设计问题。PHP作为流行的服务器端脚本语言,也有其特定的设计模式应用。本文将重点介绍几种PHP中常用的设计模式,包括单例模式、工厂模式和策略模式,并通过实际代码示例展示它们的具体用法。同时,我们还将讨论如何在实际项目中合理选择和应用这些设计模式,以提升代码的可维护性和扩展性。
150 4
|
7月前
|
SQL 安全 PHP
PHP开发中防止SQL注入的方法,包括使用参数化查询、对用户输入进行过滤和验证、使用安全的框架和库等,旨在帮助开发者有效应对SQL注入这一常见安全威胁,保障应用安全
本文深入探讨了PHP开发中防止SQL注入的方法,包括使用参数化查询、对用户输入进行过滤和验证、使用安全的框架和库等,旨在帮助开发者有效应对SQL注入这一常见安全威胁,保障应用安全。
221 4
|
8月前
|
JavaScript Java PHP
快速对比:Django、Spring Boot、Node.js 和 PHP
快速对比:Django、Spring Boot、Node.js 和 PHP
381 7
|
9月前
|
设计模式 算法 PHP
PHP中的设计模式:策略模式的深入探索与实践在软件开发的广袤天地中,PHP以其独特的魅力和强大的功能,成为无数开发者手中的得力工具。而在这条充满挑战与机遇的征途上,设计模式犹如一盏明灯,指引着我们穿越代码的迷雾,编写出更加高效、灵活且易于维护的程序。今天,就让我们聚焦于设计模式中的璀璨明珠——策略模式,深入探讨其在PHP中的实现方法及其实际应用价值。
策略模式,这一设计模式的核心在于它为软件设计带来了一种全新的视角和方法。它允许我们在运行时根据不同情况选择最适合的解决方案,从而极大地提高了程序的灵活性和可扩展性。在PHP这门广泛应用的编程语言中,策略模式同样大放异彩,为开发者们提供了丰富的创作空间。本文将从策略模式的基本概念入手,逐步深入到PHP中的实现细节,并通过一个具体的实例来展示其在实际项目中的应用效果。我们还将探讨策略模式的优势以及在实际应用中可能遇到的挑战和解决方案,为PHP开发者提供一份宝贵的参考。
|
9月前
|
设计模式 SQL 安全
PHP中的设计模式:单例模式的深入探索与实践在PHP的编程实践中,设计模式是解决常见软件设计问题的最佳实践。单例模式作为设计模式中的一种,确保一个类只有一个实例,并提供全局访问点,广泛应用于配置管理、日志记录和测试框架等场景。本文将深入探讨单例模式的原理、实现方式及其在PHP中的应用,帮助开发者更好地理解和运用这一设计模式。
在PHP开发中,单例模式通过确保类仅有一个实例并提供一个全局访问点,有效管理和访问共享资源。本文详细介绍了单例模式的概念、PHP实现方式及应用场景,并通过具体代码示例展示如何在PHP中实现单例模式以及如何在实际项目中正确使用它来优化代码结构和性能。
118 2
|
9月前
|
SQL 关系型数据库 数据库连接
php连接数据库之PDO,PDO的简单使用和预定义占位符的使用以及PDOStatement对象的使用,占位符的不同形式,bindValue和bindParam绑定预定义占位符参数的区别
本文介绍了PHP中PDO(PHP Data Objects)扩展的基本概念和使用方法。内容包括PDO类和PDOStatement类的介绍,PDO的简单使用,预定义占位符的使用方法,以及PDOStatement对象的使用。文章还讨论了绑定预定义占位符参数的不同形式,即bindValue和bindParam的区别。通过具体示例,展示了如何使用PDO进行数据库连接、数据查询、数据插入等操作。
php连接数据库之PDO,PDO的简单使用和预定义占位符的使用以及PDOStatement对象的使用,占位符的不同形式,bindValue和bindParam绑定预定义占位符参数的区别