Modern PHP 笔记(二):良好实践

系列笔记:
Modern PHP 笔记(一):语言特性
Modern PHP 笔记(二):良好实践
Modern PHP 笔记(三):部署测试和调优

相比于上一篇笔记Modern PHP 笔记(一):语言特性,第二部分侧重讲标准和良好实践,更加实战。

第三章:标准

PHP-FIG(PHP Framework Interop Group)2009年

框架的互操作性

通过接口、自动加载、代码风格,使框架相互合作

PSR标准

PSR: PHP Standard Recommendation(PHP推荐标准)

可以看点击链接看详细的中文版。很多方案在起草中,也有废弃的,比如PSR-4就是替代PSR-0的

第四章:组件

多用组件,从框架的封闭生态中走出来,减少重复造轮子。

组件是什么?

打包的代码,一系列相关的类、接口、性状

组件与框架对比

框架(尤其是较旧的框架)的问题是:需要更多投入,框架提供很多工具,但是可能缺少我们需要的。
把第三方代码集成上去费时费力。框架本身的维护也需要很多人力。

使用正确的工具做正确的事

  • 如果是一些PHP组件就能搞定的小项目,用组件。
  • 多个团队成员开发,用框架。它能提供约定、准则、结构等,加速开发。

查找组件

组件库Packagist https://packagist.org/

优秀组件可参考Awesome PHP:https://github.com/ziadoz/awesome-php

使用组件

使用composer下载并自动加载依赖组件

使用composer

composer require vendor/package

composer.lock文件会列出使用的所有组件和具体版本号,确保组件版本一致。

composer install // 不会更新.lock
composer update  // 更新.lock到最新版

自动加载

只需要一句,不用一一引入

require 'vendor/autoload.php'

composer和私有仓库

不方便开源的组件也可以使用composer

创建PHP组件

  1. 取一个厂商名和包名

  2. 定一个命名空间

  3. 文件系统

     src/    源码
     tests/     测试
     composer.json   composer的配置文件,描述组件、依赖等
     README.md
     CONTRIBUTING.md   说明如何为这个组件做贡献
     LISENSE      组件许可证
     CHANGELOG.md   每个版本引入的改动
    
  4. composer.json

     require 列出需要的最小依赖
     require-dev 列出开发组件需要的依赖
    
  5. 实现组件

  6. 发到版本控制

  7. 提交到 Packagist

  8. 使用组件 composer require

第五章:良好实践

过滤、验证和转义

不要相信任何人。

包括:

  • $_GET
  • $_POST
  • $_REQUEST
  • $_COOKIE
  • $argv
  • php://stdin
  • php://input
  • file_get_contents()
  • 远程数据库
  • 远程API
  • 来自客户端的数据

过滤输入

转义或删除不安全的字符。

HTML

使用htmlentities()函数,但是它默认不会转义单引号,正确使用方式:第一个参数是输入字符串,
第二额参数设为ENT_QUOTES常量,转义单引号,第三个参数设为输入字符串的字符集。

$htmlentities($input, ENT_QUOTES, 'UTF-8');

如果需要更多过滤HTML方式,可以使用HTML Purifier库。

SQL查询

使用PDO预处理语句

用户资料信息

filter_var()filter_input()函数

  • 过滤电子邮件 filter_var($email, FILTER_SANITIZE_EMAIL);

  • 过滤用户资料中的外国字, 删除小于ASCII 32的字符,转义大于ASCII 127的字符

      filter_var(
          $string,
          FILTER_SANITIZE_STRING,
          FILTER_FLAG_STRIP_LOW|FILTER_FLAG_ENCODE_HIGH);
    

验证数据

区别于过滤数据会删除,验证不会,只是确保符合特定格式。
传递FILTER_VALIDATE_*标志给filter_var()函数。验证失败返回false,否则返回原值。

    //验证电子邮件
    $input = 'john@example.com'
    $isEmail = filter_var($input, FILTER_VALIDATE_EMAIL);
    if ($isEmail !== false) {
        echo 'success';
    } else {
        echo "fail";
    }

推荐组件:

  • aura/filter
  • respect/validation
  • symsony/validator

转义输出

很多模板引擎(e.g., twig、smarty)会自动转义,否则使用htmlentities($output, ENT_QUOTES, 'UTF-8');

密码

  • 绝对不能知道用户的密码
  • 绝对不能约束用户的密码
  • 绝对不能通过电子邮件发送用户的密码

使用bcrypt计算用户密码的哈希值

应该用哈希,而不是加密。加密是双向算法,可以解密,而哈希是单向算法。
哈希算法很多,一般用bcrypt,与MD5和SHA1不同,bcrypt故意设计的很慢,多次处理数据(工作因子)。

密码哈希API

注册用户

     // 验证电子邮件
     // 验证密码
     // 创建密码哈希值
     // 创建用户账户
     // 重定向登录账户

登录用户

     // 从请求主体获取电子邮件地址
     // 从请求主体获取密码
     // 使用电子邮件查询账户

     password_verify();
     // 验证密码和账户哈希值是否匹配

     password_needs_rehash();
     // 如果需要,重新计算哈希值(最新的算法)
     // 把登录状态保存在会话中
     // 重定向到个人资料页

PHP5.5.0之前,使用组件ircmaxell/password-compat

日期、时间和时区

设置默认时区

// php.ini
date.timezone = 'America/New_York';

// *.php
date_default_timezone_set('America/New_York');

DateTime类

构造$datetime = new DateTime('2014-09-08 5:03 AM');
自定义格式DateTime::createFromFormat('M j, Y H:i:s', 'Jan 2, 2014 23:09:12')

DateInterval类

表示固定的时间段,DateInterval用于修改DateTime实例.

    $datetime = new DateTime('2014-09-08 5:03 AM');
    $interval = new DateInterval('P2W');
    $datetime->add($interval);
    echo $datetime->format('Y-m-d H:i:s');

DateTimeZone类

    $timezone = new DateTimeZone('America/New_York');
    $datetime = new DateTime('2014-09-08 5:03 AM', $timezone);

DatePeriod类

一段时期内反复出现的一系列日期和时间

    $period = new DatePeriod($startDateTime, $interval, 3);

nesbot/carbon组件

数据库

PDO扩展,PHP Data Objects,PHP数据对象,是一系列类,抽象了不同数据库的具体实现。

这里实际上现在框架的ORM用的很好,不需要直接PDO了。

    // 预处理语句
    $sql = 'select id from user where email = :email';
    $statement = $pdo->prepare($sql);

    $email = filter_input(INPUT_GET, 'email');
    $statement->bindValue(':email', $email);
    // ':email'是占位符

    // 查询结果
    $statement->execute();

    // 迭代结果
    while (($result = $statement->fetch(PDO::FETCH_ASSOC)) !== false) {
        echo $result['email'];
    }

事务

    $pdo->beginTransaction();
    // sql
    $pdo->commit();

多字节字符串

使用mbstring扩展,比如用mb_strlen()代替原生的strlen()

处理字符编码:

  • 一定要知道数据的字符编码

  • 使用utf-8存储数据

  • 使用utf-8输出数据

      // php.ini
      default_charset = 'UTF-8';
    

流的作用是在出发地和目的地之间传输数据。出发地和目的地可以是文件、命令行进程、网络连接、
ZIP或TAR压缩文件、临时内存、标准输入和输出、或者是PHP流封装协议实现的其他资源。就是管道

流封装协议

流协议和目标格式:

    <scheme>://<target>

下面例子里file_get_contents()的字符串参数是流标识符,只是因为HTTP流协议就是这样,像URL

    $json = file_get_contents(
        'http://api.flickr.com....'
    )

还有其他file://流封装协议、php://流封装协议

流上下文

有些流接受的可选参数,成为流上下文
使用stream_context_creat()函数创建

流过滤器

流真正的强大之处:过滤、转换、添加、删除流中传输的数据。
string.toupper等,大多需要自定义stream_filter_append()

自定义流过滤器

自定义流过滤器是个PHP类,扩展内置的php_user_filter类,实现一些方法,然后用
stream_filter_register()注册。

错误和异常

错误会导致脚本停止,异常时PHP错误处理面向对象的产物,异常要先实例化,然后抛出,最后再捕获。

异常

异常是Exception类的对象。
$exception = new Exception('msg', -1);
抛出异常throw new Exception('msg', -1);,异常抛出后程序会终止。
捕获异常try {...} catch { }

异常处理程序

设置一个全局异常处理程序,捕获所有未捕获的异常。

    set_exception_handler(function (Exception $e) {
        // 处理并记录异常
    });

    // 编写其他代码

    // 还原之前的异常处理程序
    restore_exception_handler();

错误

在开发环境,显示并记录所有错误信息;

在生产环境,记录大多数错误信息,但不显示出来;

    set_error_handler();  // 全局错误处理程序
    restore_error_handler();

在开发环境中处理错误和异常

使用Whoops组件

在生产环境处理错误和异常

使用Monolog组件

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,227评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,755评论 1 298
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,899评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,257评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,617评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,757评论 1 221
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,982评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,715评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,454评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,666评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,148评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,512评论 3 258
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,156评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,112评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,896评论 0 198
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,809评论 2 279
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,691评论 2 272

推荐阅读更多精彩内容

  • 良好实践,这次主要挑了一些开发PHP应用时应该运用上的良好实践进行详细记录,特别是良好实践部分中密码和流两个点。关...
    BeckJiang阅读 2,077评论 6 41
  • Composer Repositories Composer源 Firegento - Magento模块Comp...
    零一间阅读 3,941评论 1 66
  • Welcome 目前网络上充斥着大量的陈旧信息,让PHP新手误入歧途,传播着错误的实践和糟糕的代码,这必须得到纠正...
    layjoy阅读 21,600评论 7 118
  • 接下来他要闯过的第二关就是成语大关卡。你们觉得他能不能通过呢?我希望他能通过吧,首先他要通过的第一个成语,约翰正在...
    天天快乐_07cd阅读 310评论 3 0
  • 如果有些东西停留在表面, 会让我很不安, 是我出了问题么
    张小凡Carrie阅读 192评论 0 0