• PHP|开发必知的良好实践


    过滤、验证、转义

    所有这些外部资源都不能完全相信

    • $_GET

    • $_POST

    • $_REQUEST

    • $_COOKIE

    • $argv

    • php://stdin

    • php://input

    • file_get_contents()

    • 远程数据库

    • 远程API

    • 客户端的数据

    过滤

    使用htmlentities()过滤HTML,将特殊字符转换为HTML实体,转义输出,第二个参数使用ENT_QUOTES

    使用PDO预处理语句过滤SQL注入.

    使用filter_var() & filter_input()函数来过滤和验证不同类型的输入。

    Eg: email, number, char, 特殊字符

    密码

    最安全的哈希算法:bcrypt

    使用:

    password_hash()
    password_get_info()
    password_needs_rehash()
    password_verify()

    函数来生成密码。

    日期,时间,时区

    使用PHP5.2.0引入的DateTime & DateInterval & DateTimeZone类来处理时间。

    设置默认时区

    在php.ini中,设置

    date.timezone = 'country/city'
    

    使用date_default_timezone_set()函数来设置默认时区。

    DateTime Class

    使用DateTime类来管理日期和时间

    $datetime = new DateTime();

    使用DateInterval类来构造长度固定的时间段。配合上一个类中的方法使用。

    DateTimeZone Class

    时区的处理选择。

    $timezone = new DateTimeZone('Asia/Shanghai');
    
    $datetime = new DateTime('2016-05-20', $timezone);
    
    $datetime->setTimezone(new DateTimeZone('Asia/Hong_Kong'));

    有时,我们需要迭代处理一段时间内反复出现的一系列日期和时间,重复在日程表中记事就是个好例子。DatePeriod类可以解决这种问题

    DatePeriod实例就是迭代器,每次迭代会产出一个DateTime实例。

    <?php
    $start = new DateTime();
    $interval = new DateInterval('P2W');
    $period = new DatePeriod($start, $interval, 3);
    
    foreach ($period as $nextDateTime) {
        echo $nextDateTime->format('Y-m-d H:i:s'), PHP_EOL;
    }

    DB

    PDO扩展

    http://php.net/manual/zh/pdo.drivers.php

    <?php
    try {
        $pdo = new PDO(
            'mysql:host=127.0.0.1;dbname=books;port=3306;charset=utf8',
            'USERNAME',
            'PASSWORD'
        );
    } catch (PDOException $e) {
        echo "Fail";
        exit;
    }

    预处理语句

    为了防止SQL注入,使用PDO的预处理语句。

    预处理语句时PDOStatement的实例。可以通过PDO实例的prepare()方法获取与处理语句对象。

    $sql = "select id from users where email = :email";
    $statement = $pdo->prepare($sql);
    
    $email = filter_input(INPUT_GET, 'email');
    $statement->bindValue(':email', $email);

    http://php.net/manual/pdo.constants.php

    继续:

    $statement->execute();
    
    while (($result = $statement->fetch(PDO::FETCH_ASSOC)) !== false) {
        echo $result['email'];
    }
    
    $results = $statement->fetchAll(PDO::FETCH_ASSOC);
    foreach ($results as $result) {
        echo $result['email'];
    }

    PDO的事务

    try {
        $pdo = new PDO();
    }catch(PDOExcetion $e){
        //
    }
    
    $stmtSubtract = $pdo->prepare('
        UPDATE accounts
        SET amount = amount - :amount
        WHERE name = :name
    ');
    $stmtAdd = $pdo->prepare('
        UPDATE accounts
        SET amount = amount + :amount
        WHERE name = :name
    ');
    
    $pdo->beginTransaction();
    
    $fromAccount = 'Checking';
    $withdrawal = 50;
    $stmtSubtract->bindParam(':name', $fromAccount);
    $stmtSubtract->bindParam(':amount', $withDrawal, PDO::PARAM_INT);
    $stmtSubtract->execute();
    
    $toAccount = 'Savings';
    $deposit = 50;
    $stmtSubtract->bindParam(':name', $$toAccount);
    $stmtSubtract->bindParam(':amount', $deposit, PDO::PARAM_INT);
    $stmtSubtract->execute();
    
    $pdo->commit();

    多字节字符串

    PHP假设字符串中的每个自负都是八位字符,占一个字节的内存。然而考虑到国家化的时候,一个字符就不只占用一个字节了。

    为了避免处理多字节字符串出错,可以安装mbstring扩展。替换PHP原生的函数。

    关于字符编码

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

    • 使用UTF8存储数据

    • 使用UTF8输出数据

    php.ini中设置,告诉PHP使用UTF8:

    default_charset = "UTF-8";
    

    很多PHP函数都使用这个默认的字符集:

    htmlentities()
    html_entity_decode()
    htmlspecialchars()
    以及mbstring中的扩展函数
    

    流在PHP4.3.0中引入,作用是使用统一的方式处理文件,网络和数据压缩等共用同一套函数和用法的操作。简单而言,流是具有流式行为的资源对象。因此,流可以线性读写,或许还能使用fseek()函数定位到流中的任何位置。

    流的作用实际上是在出发地和目的地之间传输数据。出发地和目的地可以是

    文件,命令行进程,网络连接,zip, TAR压缩, 临时内存,标准输入输出,或者是通过php流封装协议实现的资源(http://php.net/manual/wrappers.php)

    流封装协议

    http://php.net/manual/wrappers.php

    流封装协议的作用是使用通用的接口封装读写文件系统的差异。

    每个流都有一个协议和目标

    <scheme>://<target>
    

    file:// 流封装协议

    • file_get_contents

    • fopen

    • fwrite

    • fclose

    <?php
    // 隐式
    $handle = fopen('/etc/hosts', 'rb');
    while (feof($handle) !== true) {
        echo fgets($handle);
    }
    fclose($handle);
    
    // 显式
    $handle = fopen('file:///etc/hosts', 'rb');
    while (feof($handle) !== true) {
        //
    }
    
    fclose($handle);

    php://流封装协议

    • php://stdin

    • php://stdout

    • php://memory

    • php://temp

    自己编写流封装协议

    PHP提供了一个示例streamWrapper类,编写自定义的流封装协议。

    流上下文

    有些PHP流能接受一系列可选的参数,这些参数叫流上下文,用于定制流的行为。

    使用

    stream_context_create()
    

    函数创建。

    <?php
    $requestBody = '{"username": "josh"}';
    $context = stream_context_create([
        'http' => [
            'method' => 'POST',
            'header' => "",
            'content' => $requestBody,
        ],
    ]);
    
    $response = file_get_contents('http://x/xapi', false, $context);

    流过滤器

    把过滤器附加到现有的流上,使用

    stream_filter_append()
    
    <?php
    $handle = fopen('file', 'rb');
    stream_filter_append($handle, 'string.toupper');
    while (feof($handle) !== true) {
        echo fgets($handle);
    }
    
    fclose($handle);

    还可以使用php://filter流协议把过滤器附加到流上。

    $handle = fopen('php://filter/read=string.toupper/resource=data.txt', 'rb');
    while () {
    
    }
    filter/read=<filter_name>/resource=<scheme>://<target>
    

    我们还可以使用php_user_filter类来自定义流过滤器

    http://php.net/manual/en/class.php-user-filter.php

    class DirtyWordsFilter extends php_user_filter
    {
        public function filter($in, $out, &$consumed, $closing)
        {
            $words = ['grime', 'dirt', 'grease'];
            $wordData = [];
            foreach ($words as $word) {
                $replacement = array_fill(0, mb_strlen($word), '*');
                $wordData[$word] = implode('', $replacement);
            }
            
            $bad = array_keys($wordData);
            $good = array_values($wordData);
            
            while ($bucket = stream_bucket_make_writeable($in)) {
                $bucket->data = str_replace($bad, $good, $bucket->data);
                
                $consumed += $bucket->datalen;
                
                stream_bucket_append($out, $bucket);
            }
            
            return PSFS_PASS_ON;
        }
    }

    然后,我们使用stream_filter_register()函数注册这个自定义的流过滤器

    stream_filter_register('dirty_words_filter', 'DirtyWordsFilter');

    异常处理示例

    try {
        //
    } catch (PDOException $e) {
    
    } catch (Exception $e) {
        // 捕获除了PDOException之外的所有异常
    } finally {
        // 最终执行
    }

    异常处理程序

    PHP允许我们注册一个全局异常处理程序,捕获所有未被捕获的异常。

    set_exception_handler(function (Exception $e) {
        //
    });

    在某些情况下,代码执行完毕后,还需要还原成前一个异常处理程序。

    restore_exception_handler()
    
    <?php
    // register
    set_exception_handler(function (Exception $e) {
        // Process exception
    });
    
    // Coding
    
    // restore
    restore_exception_handler();

    错误

    PHP由于某种原因导致无法运行,通常会触发错误。

    我们可以使用

    trigger_error()
    

    自己触发错误。

    我们可以使用

    error_reporting()
    

    或者php.ini中使用error_reporting指令,告诉PHP如何处理错误。

    一定要遵守下述四个原则

    • 一定要让PHP报告错误

    • 在开发环境中显示错误

    • 在生产环境中不能显示错误

    • 都要记录错误

    php.ini中:

    //Dev
    display_startup_errors = On
    display_errors = On
    
    error_reporting = -1
    log_errors = On
    
    //Prod
    display_startup_errors = Off
    display_errors = Off
    
    error_reporting = E_ALL & ~E_NOTICE
    log_errors = On

    错误处理的函数

    set_error_handler(function ($error, $errstr, $errfile, $errline) {
        // Process error
    })

    我们也可以把PHP的错误转换为异常

    只能转换满足php.ini中error_reporting指令设置的错误

    set_error_handler(function ($errno, $errstr, $errfile, $errline) {
        if (!(error_reporting() & $errno)) {
            return
        }
        
        throw new ErrorException($errstr, $errno, 0, $errfile, $errline);
    });

    参考

    1. Modern PHP

  • 相关阅读:
    Linux:CentOS-7配置VMware-15.5与本机IP同网段
    SpringCloud:Zuul路由配置超时问题
    SpringCloud:路由ZUUL的配置详解
    SpringCloud:扩展zuul配置路由访问
    给source insight添加.cc的C++文件后缀识别(转载)
    Linux下共享库嵌套依赖问题 (转载)
    Ubuntu 下编译libjingle-0.6.14 (转载)
    什么是「穷人思维」?
    谁上北大是能力说了算,不该是教育部(转载)
    Installing cmake 2.8.8 or higher on Ubuntu 12.04 (Precise Pangolin) (转载)
  • 原文地址:https://www.cnblogs.com/chunguang/p/5581392.html
Copyright © 2020-2023  润新知