-
Notifications
You must be signed in to change notification settings - Fork 3
Description
现代PHP
这是 PHP 之道项目学习笔记。
本文除了对项目「PHP 之道」中所阐述的技术外,加入了自己的理解,并修复的部分无效资源链接地址和新增了一些内容
Windows 安装 PHP
PHP 内置 Web 服务器
自 PHP 5.4 版本器, CLI SAPI 提供内置的 Web 服务器,主要用于本地开发,不可用于线上开发环境。
启动内置 Web 服务器,需要进入项目根目录,执行命令
php -S localhost:3000代码风格指南
当前还没有官方出品的编码规范,不过 PHP 标准组提出了实践编码标准,也是蛮多 PHP 社区的事实编码标准,在开发过程中遵循这些标准即可
编码标准
@ TODO 阅读规范
- PSR-0 标准: 自动加载规范 [已废弃]
- PSR-1 标准: 基础编码规范
- PSR-2 标准: 编码风格规范
- PSR-3 标准: 日志接口规范
- PSR-4 标准: 自动加载规范
- PSR-6 标准: 缓存接口规范
- PSR-7 标准: HTTP 消息接口规范
- 阅读 PEAR 编码准则
- 阅读 Symfony 编码准则
- 阅读 Symfony 编码准则[译文]
编码标准检测工具
PHP_CodeSniffer 工具可以检测代码是否符合这些编码标准,还有 Sublime Text 检测插件,Visual Studio Code 插件。
PHP Code Siniffer 简单使用
- 安装
全局和局部安装 PHP Code Siniffer 需要确保
1.1 全局安装
执行命令安装 PHP Code Siniffer
pear install PHP_CodeSniffer1.2 局部安装(项目内安装)
执行命令安装 PHP Code Siniffer
composer require --dev squizlabs/php_codesniffer1.3 PHP_CodeSniffer 基本用法
1.3.1 检测需要修复的文件
phpcs file/to/sniff
# 以 PSR2 规范检测响应需要修复的方法
phpcs -sw --standard=PSR2 file/to/sniff
#1.3.2 使用 PHP_CodeSniffer 美化修整器
phpcbf -w --standard=PSR2 file/to/sniff资源
PHP CodeSniffer - 开源项目
PHP CodeSniffer - 开源项目 Wiki
PHP代码规范与质量检查工具PHPCS,PHPMD的安装与配置
编码标准修正工具
- PHP Coding Standards Fixer
- 另一个是随 PHP_CodeSniffer 安装的 PHP Code 美化修整器
PHP 语言精粹
里程碑
- PHP 5.0 完善面向对象
- PHP 5.3 新增匿名函数与命名空间
- PHP 5.4 支持 traits
TODO PHP 面向对象编程
PHP 有用完整的面向对象功能特性
- 类
- 抽象类
- 接口
- 继承
- 构造函数
- 克隆
- 异常
TODO Trait
函数式编程 (Functional Programming)
PHP 支持函数是 「第一等公民」 ,体现在:
- 函数可以赋值给变量(包括内置和用户定义函数都可以赋值给变量)。
- 函数可以作为参数传递给其它函数(称为 高阶函数)。
- 也可作为函数返回值返回。
- 支持递归和迭代。
PHP 函数式编程
匿名函数
Closure 类
Closure 类,及代表匿名函数的类
Callable 类型
动态调用函数方法 call_user_func_array()
语法: mixed call_user_func_array(callable $callback, array $param_array)
$callback: 最为回调函数调用
$param_array: 最为回调函数的参数传入
<?php
function add($arg1, $arg2)
{
printf("Call function add, add %s + %s\n", $arg1, $arg2);
}
class Add
{
public function doAdd($arg1, $arg2)
{
return $arg1 + $arg2;
// printf("Call class Add::doAdd, add %s + %s", $arg1, $arg2);
}
}
call_user_func_array("add", array(1, 2));
$result = call_user_func_array(array(new Add(), "doAdd"), array(3, 4));
var_dump("Call Add::doAdd result:" . $result);函数式编程参考资料
TODO
元编程
通过使用反射 API 和魔术函数,可以实现 PHP 的元编程功能。
反射 API
重载
魔术函数
命名空间
不同开发者开发的库,可能有使用相同类名的可能,这就导致冲突异常命名空间
的功能引入就是解决该问题。
命名空间类似操作系统的目录,同一目录下不能创建两个同名的文件;同理,两个相同
命名空间内不能创建相同的 PHP 类。
在编写代码时,将代码放在自定义命名空间下,可避免与第三方类库冲突。
命名空间
PSR-4 标准: 自动加载规范
本规范除定义了自动加载相关内容,还提供了命名空间的推荐使用方式,它提供一个标准的文件、类和命名空间的使用惯例,进而让代码做到随插即用。仔细阅读
PHP 标准库
PHP 标准库(Standard PHP Library 简称 SPL),随 PHP 一起发布,提供一组类和接口。
包含常用的数据结构类(堆栈,队列,堆等),及遍历这些数据接口的迭代器。
数据结构
- SplDoublyLinkedList : 双向链表
- SplStack: 堆栈
- SplQueue: 队列
- SplHeap: 堆
- SplMaxHeap: 最大堆
- SplMinHeap: 最小堆
- SplPriorityQueue: 优先队列
- SplFixedArray: 定长数组
- SplObjectStorage: 类提供从对象到数据的映射,或通过忽略数据来提供对象集
迭代器
接口
- Todo Countable: 实现该接口可被用于
count()函数 - OuterIterator:实现该接口,可被迭代器迭代
- RecursiveIterator: 实现该接口,可被迭代器递归迭代
- SeekableIterator:The Seekable iterator
异常
文件处理
SPL 函数
其它类与接口
付费 SPL 视频教程
命令行接口
PHP 提供命令行接口(CLI),通过 PHP 命令行编程能够帮助完成自动化任务,如测试,部署和应用管理
查看 PHP 版本
php -v查看 PHP CLI 命令行选项列表
php -h查看 PHP 配置信息
php -i功能类似与 phpinfo() 函数
shell 交互式编程
php -a
# response
# Interactive shell
# php> 之后可以直接在 SHELL 环境下进行 Interactive shell 编程
php > echo 5 + 8;
13
php > function addTwo($num)
php > {
php { return $num + 2;
php { }
php > var_dump(addTwo(2));
php shell code:1:
int(4)
php >执行 PHP 脚本
语法:php scriptname.php [param1] [param2] 或者 php -f scriptname.php [param1] [param2]
使用命令,解析并执行 scriptname.php 脚本
$argc: 传递给脚本的参数数目
$argv: 传递给脚本的参数数组
注: 对于 $argc 由于脚本名(如 hello.php) 总是作为参数传递给脚本,因此 $argc 最小值为 1。
注: 对于 $argv 由于第一个参数总是当前脚本的文件名(如 hello.php),因此 $argv[0] 就是脚本文件名(如 hello.php)。
举例:创建文件名为 hello.php 的脚本
<?php
if($argc != 2) {
echo "Usage: php hello.php [name].\n";
}
$name = $argv[1];
echo "hello, $name\n";执行命令:
php hello.php huliuqing
## or
php -f hello.php huliuqing
# result
hello, huliuqingXDebug 调试工具学习
XDebug 是一个 PHP 调试工具,可以在很多 IDE 中做[ TODO 断点调试],[ TODO 堆栈检查],也可以进行代[ TODO 码覆盖检查]和[ TODO 性能跟踪]。
安装 XDebug
入门 XDebug
XDebug 远程调试功能
依赖管理
Composer
Composer
Composer 是 PHP 项目的依赖管理工具,用于管理项目中的 packagies 和 libraries ,使用Composer 安装的依赖库安装与 vendor 目录。
开发实践
基础知识学习
日期和时间函数
TODO DateTime 类
TODO DateInterval 类
TODO DatePeriod 类
使用 Carbon 组件
设计模式
使用 UTF-8 编码
PHP 使用 UTF-8
在 PHP 中对于字符串拼接或赋值操作,并不需要对 UTF-8 做特别处理。
但是如果是对字符串定位如 strpos(),或求字符串长度 strlen() 则需要特别处理。
函数名类似 mb_* 的TODO 多字节字符串函数就是专门为 Unicode 字符串而特别进行设计。
在编写 PHP 脚本时,应该在脚本开头使用 TODO mb_internal_encoding("UTF-8") 函数设置内部字符编码,并在对浏览器输出显示是使用 TODO mb_http_output() 获取 HTTP 输出字符编码
patchwork/utf8: 该工具会在 mbstring 可用时自动使用,否则自动切换回非 UTF-8 函数。
数据库使用 UTF-8
浏览器使用 UTF-8
在 PHP 内我们保证使用 mb_http_output() 确保向浏览器输出 UTF-8 编码的字符串。
之后,我们可以通过两种方式设置浏览器 UTF-8 编码
- 在 HTML 文件的
TODO 字符集 标签 - 在 PHP 脚本里设置
Content-Type响应头
举例
<?php
//告知 PHP 我们在此次使用的是 UTF-8 编码的字符
mb_internal_encoding("UTF-8");
//告知 PHP 我们在输出浏览器时使用 UTF-8 编码
mb_http_output("UTF-8");
// Our UTF-8 test string
$string = 'Êl síla erin lû e-govaned vîn.';
// Transform the string in some way with a multibyte function
// Note how we cut the string at a non-Ascii character for demonstration purposes
$string = mb_substr($string, 0, 15);
header('Content-Type: text/html; charset=UTF-8');
?><!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>UTF-8 test page</title>
</head>
<body>
<?php
echo $string;
?>
</body>
</html>字符串处理更多资料
TODO
- PHP 手册:字符串运算符
- PHP 手册:字符串函数
- PHP 手册:多字节字符串函数
- mb_strpos()
- mb_strlen()
- mb_substr()
- mb_internal_encoding(): 设置/获取内部字符编码
- mb_http_output(): 设置/获取 HTTP 输出字符编码
- htmlentities()
- htmlspecialchars()
- PHP UTF-8 Cheatsheet
- [Handling UTF-8 with PHP]http://www.phpwact.org/php/i18n/utf-8()
- Stack Overflow: What factors make PHP Unicode-incompatible?
- Stack Overflow: Best practices in PHP and MySQL with international strings
- How to support full Unicode in MySQL databases
- Bringing Unicode to PHP with Portable UTF-8
- Stack Overflow: DOMDocument loadHTML does not encode UTF-8 correctly
国际化(I18N) 和本地化(L10N)
TODO PHP 之道 : 国际化(i18n)和本地化(l10n)
概念
I18N: internationalization(国际化),指一开始设计一个支持多语言的架构。
L10N: Localization(本地化),指的是新语言的添加。基于 I18N 的架构设计,在每次新增语言时,新增对语言的翻译
Pluralization: 复数形式,不同语言复数规则不一样
TODO 阅读 国际化(i18n)和本地化(l10n) 实现
TODO PHP 之道 : 国际化(i18n)和本地化(l10n)
依赖注入
数据库
数据库驱动
PDO
什么是 PDO
PDO是一个数据库连接抽象库,通过提供对不同数据库交互的通用接口。
使用模版
在 MVC 软件架构模式中,模版即为「试图(V: View)」,提供了展现逻辑与业务逻辑分离的能力。
为什么使用模版?
- 将项目的呈现逻辑与业务逻辑分离,模版完成对内容的展现。
- 实现团队分工合作,前端工程师实现前端展现逻辑,服务端工程师实现代码业务逻辑和模型控制
- 改善前端架构,模版放在「试图」文件夹内,能够将大块的代码查分称小块的组件实现组件复用。
- 能够使用专业的前端模版类库,类库能够提供对用户内容的有效处理,提升项目安全性
原生 PHP 模版
原生 PHP 模版:指在前端页面(通常是 HTML 页面)内混用 PHP 标签,进行混合编程
优点是相对 PHP 研发人员无需学习新的语法,没有编译过程,速度更快;但是会使 前端 HTML 与服务端业务逻辑杂糅在一起,不利于后期维护和开发。
编译模版
尽管 PHP 不断升级为成熟的、面向对象的语言,但它作为模板语言 没有改善多少。
广泛使用的编译模版有:
编译模版优点:
- 提供专门的模版语法
- 自动转义,提供模版继承功能实现复用
- 简化控制结构
- 提升代码可读性和维护性
编译模版不足,由于需要编译会带来性能上的影响;解决方案为对编译后的文件进行缓存
mustache 语言
更多资源
文章与教程
Templating Engines in PHP
An Introduction to Views & Templating in CodeIgniter
Getting Started With PHP Templating
Roll Your Own Templating System in PHP
Master Pages
Working With Templates in Symfony 2
Writing Safer Templates
类库
Aura.View (native)
Blade (compiled, framework specific)
Brainy (compiled)
Dwoo (compiled)
Latte (compiled)
Mustache (compiled)
PHPTAL (compiled)
Plates (native)
Smarty (compiled)
Twig (compiled)
Zend\View (native, framework specific)
错误与异常
PHP 是一个「轻异常」(exception light) 语言,当在执行过程中 PHP 会尽量忽略异常,除非遇到严重错误
如
$ php -a
php> echo $foo;
## throw notice exception
PHP Notice: Undefined variable: foo in php shell code on line 1PHP 在上面的例子中将抛出一个 Notce 级别的异常错误,但是 PHP 会继续执行脚本。
常用错误级别
- Error: 错误异常,将中断脚本执行,必须修复;常量
E_ERROR - Notice: 通知异常,建议性的异常信息,脚本不会中断执行;常量
E_NOTICE - Warning: 警告异常,非致命的错误,也不会中断执行;常量
E_WARNING
修改 PHP 错误报告行为
使用 error_reporting() 函数设置程序在执行期间的错误等级,在程序执行过程中将仅显示
设定的异常消息
<?php
error_reportint(E_ERROR | E_WARNING);控制错误消息输出方式
除了将错误消息直接输出到屏幕,还可以通过设置将错误消息记录到日志中,请查看 TODO Error Reporting(错误报告)
行内错误抑制
通过错误控制操作符 @ ,可以抑制异常输出
echo @$foo;但是别这样处理,原因有
- 所有的错误都不会输出到屏幕
- 所有的错误都不会记录到错误日志
关闭 @
原生 PHP 无法关闭 错误抑制操作符 @,但是可以使用 XDebug 的 xdebug.scream 的 ini 配置项关闭它
xdebug.scream = On
也可是在 PHP 脚本内,运行时设置
<?php
ini_set("xdebug.scream", 1);此外,还可以通过 Scream 来设置
资料
错误异常类
通过使用 ErrorException 类抛出 「错误」来处理异常,该类继承自 Exception 类。
资料
异常
在开发过程中使用 try{}catch(Exception $d){} 来抛出和捕捉异常处理
SPL 异常
通过继承 SPL 标准库 Exception 类处理异常操作
资料
安全
Web 程序安全
密码 Hash 处理
Hash 密码算法是单向不可能的,Hash 值是固定长度的字符串切无法推算出原始密码。
在构建 Web 应用程序或其它应用程序,最终会加入用户授权模块,这涉及到用户帐号及密码存储功能。
对密码应该单独
- 存储密码前进行密码 Hash 处理
- 使用加盐处理,以防(「字典破解」)(https://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=1&ved=0ahUKEwjiurrX0v7VAhXCGJQKHbRrBMQQFggmMAA&url=https%3A%2F%2Fzh.wikipedia.org%2Fzh-hans%2F%25E6%259A%25B4%25E5%258A%259B%25E7%25A0%25B4%25E8%25A7%25A3%25E6%25B3%2595&usg=AFQjCNGO3NW2bTMVI_fOd1sRsBskPXesrQ)或(「彩虹碰撞」)(https://zh.wikipedia.org/zh-hans/%E5%BD%A9%E8%99%B9%E8%A1%A8)
PHP Hash 密码算法函数 password_hash()
- password_hash() 创建密码的 Hash,在生成 Hash 值时函数已经处理好加盐,加入的随机子串通过加密算法自动保存, 自 PHP 5.5 引入
- password_verify() 来验证密码是否匹配 password_hash 生成的 hash 值
对于 PHP 5.3.7
资料
- 密码散列算法函数
- password_compat 5.3.7 ~ 5.5 的 密码 hash 库
- PHP password_hash() RFC
数据过滤
永远不要相信外部输入
对以下数据来源
- $_GET 表达你数据
- $_POST 表单数据
- $_SERVER 获取的超全局变量
- fopen("php://input", 'r') 得到的 HTTP 请求体
- 上传和下载的文档
- session 值
- cookie 值
- 第三方 Web 服务数据
都需要使用 filter_var() 或 filter_input(),对数据进行过滤处理
过滤策略
- 原始的外部数据传入到 HTML 页面输出,可能引发 XSS 攻击。 解决方法是,在输出前使用 strip_tags() 去除 HTML 标签 或 使用 htmlentities() 或 htmlspecialchars() 函数对特殊字符进行转义得到特殊字符的 HTML 实体
- 对由命令行传入的数据,使用 escapeshellarg() 函数进行过滤
- 通过接收外部输入来从文件系统加载文件。可已过滤调
/,../,null字符或其它敏感的、私有的、需要隐藏的文件
数据清理
数据清理:指删除或转义外部输入中的非法或不安全的字符。
- 对外部输入数据包含在 HTML 标签中的数据进行过滤
- 对使用原始 SQL 语句进行插入请求的数据进行过滤
- 对允许使用的可靠 HTML 标签采用白名单库进行校验,可以使用类似 TODO HTML Purifier 白名单库
- 除了使用白名单库,可以使用 TODO Markdown 或 TODO BBCode 这些更严格的规则进行过滤
资源
TODO 查看 安全过滤器文档(Sanitization Filters)
有效性验证
对用户输入的 email 地址,手机号,年龄等等信息进行有效性校验;可以使用 TODO 校验过滤器
反序列化 Unserialization
对不可信渠道的序列化数据进行反序列化可能存在安全问题
TODO Is PHP unserialize() exploitable without any 'interesting' methods?
TODO PHP Object Injection
配置文件安全
策略:
- 配置文件不要存储在可直接访问或上传的目录
- 配置文件如果一定要存放于根目录,请以 .php 文件扩展进行重命名,这样即使可以访问也无法直接输出显示
- 对配置文件中信息进行加密或设置访问权限
- 密钥等敏感数据不要加入到版本控制中
关闭注册全局变量功能
PHP 5.4 以前如果在配置选项值中开启 register_globals 功能,可以是 $_GET, $_POST, $_REQUEST, $_COOKIE 等变量被注册为全局变量
比如 $_GET['foo'] 被注册后可直接通过 $foo 访问到,这将是不安全的。
另一个例子是:
<?php
// 当用户合法的时候,赋值 $authorized = true
if (authenticated_user()) {
$authorized = true;
}
// 由于并没有事先把 $authorized 初始化为 false,
// 当 register_globals 打开时,可能通过GET auth.php?authorized=1 来定义该变量值
// 所以任何人都可以绕过身份验证
if ($authorized) {
include "/highly/sensitive/data.php";
}
?>TODO 在 PHP 手册中了解 Register_globals
测试
单元测试
单元测试是一种编程方法来确认函数,类和方法以我们预期的方式来工作,单元测试会贯穿整个项目的开发周期。通过检查各个函数和方法的输入输出,你就可以保证内部的逻辑已经正确执行。通过使用依赖注入和编写”mock” 类以及 stubs 来确认依赖被正确的使用,提高测试覆盖率。
工具:
atoum
Kahlan
Peridot
SimpleTest
集成测试
集成测试 (有时候称为集成和测试,缩写为 I&T)是把各个模块组合在一起进行整体测试的软件测试阶段。它处于单元测试之后,验收测试之前。集成测试将已经经过了单元测试的模块做为输入模块,组合成一个整体,然后运行集成测试用例,然后输出一个可以进行系统测试的系统。
功能性测试
功能测试的工具
Selenium
Mink
Codeception 是一个全栈的测试框架包括验收性测试工具。
Storyplayer 是一个全栈的测试框架并且支持随时创建和销毁测试环境。
行为驱动开发
其他测试工具
服务器与部署
PaaS
Platform as a Service (PaaS): 提供运行服务端项目所必须的系统环境和网络架构,仅需要项目做少量配置即可运行。
虚拟或专用服务器
Nginx 和 PHP-FPM
PHP 通过内置的 FastCGI 进程管理器(PHP-FPM)与 Nginx 服务器进行交互
资料
TODO 阅读更多 nginx 的内容
TODO 阅读更多 PHP-FPM 的内容
TODO 学习如何配置安全的 nginx 和 PHP-FPM
Nginx 和 PHP-FPM
共享主机
构建及部署应用
@ TODO
在对项目进行修改或更新的时候,我们都需要将更新重新部署新版本项目到服务器才行,这样即使一个简单的更新都需要做大量的操作
我们需要自动化构建或持续集成工具,协助完成部署工作
自动化或持续集成的任务主要体现在:
- 依赖管理
- 对静态资源的编译和压缩
- 执行测试
- 生成文档
- 打包
- 部署
构建自动化工具
自动化构建工具,及通过一系列的脚本完成项目的部署通用任务,它独立于项目之外。
-
Phing 是一种在 PHP 领域中最简单的开始自动化部署的方式。通过 Phing 你可以控制打包,部署或者测试,只需要一个简单的 XML 构建文件。Phing (基于Apache Ant) 提供了在安装或者升级 web 应用时的一套丰富的任务脚本,并且可以通过 PHP 编写额外的任务脚本来扩展
-
Capistrano 是一个为 中高级程序员 准备的系统,以一种结构化、可复用的方式在一台或多台远程机器上执行命令。对于部署 Ruby on Rails 的应用,它提供了预定义的配置,不过也可以用它来 部署 PHP 应用 。如果要成功的使用 Capistrano ,需要一定的 Ruby 和 Rake 的知识。
对 Capistrano 感兴趣的 PHP 开发者可以阅读 Dave Gardner 的博文 PHP Deployment with Capistrano ,来作为一个很好的开始。
-
Rocketeer 从 Laravel 框架中得到了很多灵感。 目标是默认智能化配置、高速、优雅的自动化部署工具。他支持多服务器,多阶段,并行部署等功能。工具的扩展性极强,并且是由 PHP 编写。
-
Deployer 是一个用 PHP 编写的部署工具,它很简单且实用。并行执行任务,原子化部署,在多台服务器之间保持一致性。为 Symfony、Laravel、Zend Framework 和 Yii 提供了通用的任务脚本。推荐阅读 Younes Rafie 的博文 快速使用 Deployer 部署 PHP 应用。
-
Magallanes 是另一个由 PHP 编写的自动化部署工具。使用 YAML 作为配置信息,支持多服务器和多环境,自动化部署。并且自带了许多通用的任务。