PHP 生态系统概述详解

PHP 诞生至今已有二十多年了。PHP 首字母缩略词最初由 Rasmus Lerdorf 于 1994 年创建,最初代表个人主页。当时,PHP 只是 C 语言中的几个通用网关接口CGI)程序,用于为简单网页提供动力。

虽然 PHP 并不是一种新的编程语言,但这个想法很流行。在九十年代后期,Zeev Suraski 和 Andi Gutmans,Zend Technologies 的联合创始人,通过重写 PHP 的整个解析器,继续了 PHP 的工作,从而诞生了 PHP3。PHP 语言名称的首字母缩略词现在代表PHP:Hypertext Preprocessor

PHP 是世界十大编程语言之一。据软件质量公司 TIOBE 称,该公司目前排名第六。在过去十年中,特别是自 2004 年 7 月 PHP5 发布以来,PHP 已被公认为构建 web 应用程序的流行解决方案。

尽管 PHP 仍然以脚本语言的形式出现,但可以肯定地说,在 PHP5 中,它远远不止于此。世界上一些最流行的平台,如 WordPress、Drupal、Magento 和 PrestaShop,都是用 PHP 构建的。正是这些项目在进一步提高 PHP 的普及率方面发挥了作用。它们中的一些通过实现复杂的 OOP(面向对象编程)设计模式扩展了 PHP 的边界,这些模式可以在其他编程语言(如 Java、C#)及其框架中找到。

尽管 PHP5 提供了良好的 OOP 支持,但仍有许多事情有待实现。PHP6 上的工作计划为 PHP Unicode 字符串提供更多支持。不幸的是,它的开发陷于停顿,PHP6 在 2010 年被取消。

同年,Facebook 发布了 HipHop 编译器。他们的编译器将 PHP 代码转换成 C++代码。C++代码通过 C++编译器进一步编译成本机代码。这个概念为 PHP 带来了重大的性能改进。然而,这种方法并不实用,因为编译 PHP 脚本到本机机器代码的过程花费了太长的时间。

不久之后,Zend Technologies 首席性能工程师 Dmitry Stogov 宣布了一个名为PHPNG的项目,该项目成为下一个 PHP 版本 PHP7 的基础。

2015 年 12 月,PHP 7 发布,带来了许多改进和新功能:

在本章中,我们将研究以下主题:

  • 为 PHP7 做准备
  • 框架

PHP7 带来了大量的变化。这些更改会影响 PHP 解释器以及各种扩展和库。尽管大部分 PHP5 代码将继续在 PHP7 解释器上正常运行,但值得了解最新可用的特性。

接下来,我们将研究其中的一些功能及其带来的好处。

标量类型提示

标量类型提示在 PHP 中并不是一个全新的特性。随着 PHP5.0 的引入,我们可以输入提示类和接口。PHP5.1 通过引入数组类型暗示扩展了这一点。后来,在 PHP5.4 中,我们还获得了输入 hint callable 的功能。最后,PHP7 引入了标量类型提示。将类型提示扩展到标量,这可能是 PHP7 中添加的最激动人心的特性之一。

以下标量类型提示现在可用:

  • string:字符串(例如,hellofoobar
  • int:整数号(如123
  • float:浮动点数(如1.22.45.6
  • bool:布尔值(例如truefalse

默认情况下,PHP7 在弱类型检查模式下工作,并将在没有投诉的情况下尝试转换为指定类型。我们可以使用strict_typesdeclare()指令控制此模式。

declare(strict_types=1);指令必须是文件中的第一条语句,否则将生成编译器错误。它只影响使用它的特定文件,不影响其他包含的文件。指令完全是编译时的,无法在运行时控制:

declare(strict_types=0); //weak type-checking
declare(strict_types=1); // strict type-checking

让我们假设下面的简单函数接受暗示的标量类型。

function hint (int $A, float $B, string $C, bool $D)
{
    var_dump($A, $B, $C, $D);
}

新标量类型声明的弱类型检查规则与扩展和内置 PHP 函数的基本相同。由于这种自动转换,在将数据传递到函数时,我们可能会在不知不觉中丢失数据。一个简单的例子是将浮点传递给需要 int 的函数;在这种情况下,转换只会去掉小数。

默认情况下,假设启用弱类型检查,则可以观察到以下情况:

hint(2, 4.6, 'false', true); 
/* int(2) float(4.6) string(5) "false" bool(true) */

hint(2.4, 4, true, 8);
/* int(2) float(4) string(1) "1" bool(true) */

我们可以看到,第一个函数调用传递的参数是提示的参数。第二个函数调用不会传递参数的确切类型,但该函数仍会在参数经过转换时执行。

假设弱类型检查关闭,通过使用declare(strict_types=1);指令,可以观察到以下情况:

hint(2.4, 4, true, 8);

Fatal error: Uncaught TypeError: Argument 1 passed to hint() must be of the type integer, float given, called in php7.php on line 16 and defined in php7.php:8 Stack trace: #0 php7.php(16): hint(2.4, 4, true, 8) #1 {main} thrown in php7.php on line 8

函数调用在第一个参数上中断,导致\TypeError异常。strict_types=1指令不允许任何类型的杂耍。参数必须与函数定义所暗示的类型相同。

返回类型提示

除了输入提示外,我们还可以输入 hint 返回。可以应用于函数参数的所有类型提示都可以应用于函数返回值。这也意味着弱类型检查规则。

要添加返回类型提示,只需在参数列表后面加上冒号和返回类型,如下例所示:

function divide(int $A, int $B) : int
{
    return $A / $B;
}

前面的函数定义表示,divide函数需要两个int类型的参数,并且应该返回一个int类型的参数。

假设弱类型检查开启,默认情况下,可以观察到以下情况:

var_dump(divide(10, 2)); // int(5)
var_dump(divide(10, 3)); // int(3)

虽然divide(10, 3)的实际结果应该是浮点,但返回类型提示会触发转换为整数。

假设弱类型检查关闭,通过使用declare(strict_types=1);指令,可以观察到以下情况:

int(5) 
Fatal error: Uncaught TypeError: Return value of divide() must be of the type integer, float returned in php7.php:10 Stack trace: #0php7.php(14): divide(10, 3) #1 {main} thrown in php7.php on line 10

strict_types=1指令就位后,divide(10, 3)失败,出现\TypeError异常。

提示

使用标量类型提示和返回类型提示可以提高代码可读性,以及自动完成 IDE 编辑器(如 NetBeans 和 PhpStorm)的功能。

匿名类

通过添加匿名类,PHP 对象获得了闭包式功能。我们现在可以通过无名类实例化对象,这使我们更接近于其他语言中的对象文字语法。让我们来看看下面的简单例子:

$object = new class {
    public function hello($message) {
        return "Hello $message";
    }
};

echo$object->hello('PHP');

前面的示例显示了一个$object变量,该变量存储对匿名类实例的引用。更可能的用法是直接将新类传递给函数参数,而不将其存储为变量,如下所示:

$helper->sayHello(new class {
    public function hello($message) {
        return "Hello $message";
    }
});

与任何普通类类似,匿名类可以将参数传递给其构造函数、扩展其他类、实现接口和使用特性:

class TheClass {}
interface TheInterface {}
trait TheTrait {}

$object = new class('A', 'B', 'C') extends TheClass implements TheInterface {

    use TheTrait;

    public $A;
    private $B;
    protected $C;

    public function __construct($A, $B, $C)
    {
        $this->A = $A;
        $this->B = $B;
        $this->C = $C;
    }
};

var_dump($object);

上述示例将输出:

object(class@anonymous)#1 (3) { ["A"]=> string(1) "A"["B":"class@anonymous":private]=> string(1) "B"["C":protected]=> string(1) "C" }

匿名类的内部名称是使用基于其地址的唯一引用生成的。

关于何时使用匿名类,没有明确的答案。它几乎完全取决于我们正在构建的应用程序和对象,取决于它们的透视图和用途。

使用匿名类的一些好处如下所示:

  • 模拟应用程序测试变得微不足道。我们可以为接口创建动态实现,避免使用复杂的模拟 API。
  • 避免为了更简单的实现而频繁调用 autoloader。
  • 让所有阅读代码的人都清楚地知道这个类在这里使用,而不是在其他地方使用。

匿名类或从匿名类实例化的对象无法序列化。尝试序列化它们会导致如下致命错误:

Fatal error: Uncaught Exception: Serialization of 'class@anonymous' is not allowed in php7.php:29 Stack trace: #0 php7.php(29): serialize(Object(class@anonymous)) #1 {main} thrown in php7.php on line 29

嵌套匿名类不会授予它访问外部类的私有或受保护方法和属性的权限。为了使用外部类保护的方法和属性,匿名类可以扩展外部类。忽略方法,如果通过匿名类的构造函数传递,则可以在匿名类中使用外部类的私有或受保护属性:

class Outer
{
    private $prop = 1;
    protected $prop2 = 2;

    protected function outerFunc1()
    {
        return 3;
    }

    public function outerFunc2()
    {
        return new class($this->prop) extends Outer
        {
            private $prop3;

            public function __construct($prop)
            {
                $this->prop3 = $prop;
            }

            public function innerFunc1()
            {
                return $this->prop2 + $this->prop3 + $this->outerFunc1();
            }
        };
    }
}

echo (new Outer)->outerFunc2()->innerFunc1(); //6

尽管我们将它们标记为匿名类,但就 PHP 引擎为这些类实例化的对象指定的内部名称而言,它们并不是真正的匿名类。匿名类的内部名称是使用基于其地址的唯一引用生成的。

语句get_class(new class{});将产生类似class@anonymous/php7.php0x7f33c22381c8的结果,其中0x7f33c22381c8 是内部地址。如果我们在代码的其他地方定义完全相同的匿名类,它的类名将不同,因为它将分配不同的内存地址。在这种情况下,生成的对象可能具有相同的属性值,这意味着它们将相等(==),但不相同(===)。

Closure::call()方法

PHP 在 5.3 版本中引入了闭包类。闭包类用于表示匿名函数。在 PHP5.3 中实现的匿名函数产生这种类型的对象。在 PHP5.4 中,闭包类有几个方法(bindbindTo)可以在匿名函数创建后对其进行进一步控制。这些方法基本上使用特定的绑定对象和类范围复制闭包。PHP7 在闭包类上引入了调用方法。call方法不会复制闭包,它会临时将闭包绑定到新 this($newThis,并使用任何给定参数调用它。然后返回闭包的返回值。

call函数签名如下所示:

function call ($newThis, ...$parameters) {}

$newThis是在call期间绑定闭包的对象。参数是可选的,将作为$parameters提供给闭包,表示零或更多。

让我们来看看下面的例子,一个简单的 Ont0.0 类和一个 Ty1-T1.闭包:

class Customer {
    private $firstname;
    private $lastname;

    public function __construct($firstname, $lastname)
    {
        $this->firstname = $firstname;
        $this->lastname = $lastname;
    }
}

$customer = new Customer('John', 'Doe');

$greeting = function ($message) {
    return "$message $this->firstname $this->lastname!";
};

echo $greeting->call($customer, 'Hello');

在实际的$greeting闭包中没有$this,直到实际绑定发生才存在。我们可以通过直接调用类似于$greeting('Hello');的闭包来确认这一点。然而,当我们通过闭包的call函数将闭包绑定到给定的对象实例时,我们假设$this将存在。在这种情况下,闭包中的$this成为customer对象实例的$this。前面的示例显示了使用call方法调用将$customer绑定到闭包。结果输出显示Hello John Doe!

发电机授权

生成器提供了一种实现迭代器的简单方法,无需实现实现迭代器接口的类的开销。它们允许我们编写代码,使用foreach在一组数据上迭代,而无需在内存中构建数组。这将消除超出内存限制的错误。它们对 PHP 并不陌生,因为它们是在 PHP5.5 中添加的。

然而,PHP7 为生成器带来了一些新的改进,其中之一就是生成器委派。

生成器委托允许生成器生成实现可遍历接口的其他生成器、数组或对象。换句话说,我们可能会说生成器委托正在产生子生成器

让我们来看看下面的例子,它有三个生成器类型的函数:

function gen1() {
    yield '1';
    yield '2';
    yield '3';
}

function gen2() {
    yield '4';
    yield '5';
    yield '6';
}

function gen3() {
    yield '7';
    yield '8';
 yield from gen1();
    yield '9';
 yield from gen2();
    yield '10';
}

// output of the below code: 123
foreach (gen1() as $number) {
echo $number;
}

//output of the below code: 78123945610
foreach (gen3() as $number) {
    echo $number;
}

生成其他生成器需要使用yield from <expression>语法。

生成器返回表达式

在 PHP7 之前,生成器函数无法返回表达式。生成器函数无法指定返回值,这限制了它们在共同例程上下文中进行多任务处理的实用性。

PHP7 使生成器能够返回表达式。我们现在可以调用$generator->getReturn()来检索return表达式。当生成器尚未返回或引发未捕获异常时调用$generator->getReturn()将引发异常。

如果生成器没有定义返回表达式并且已完成生成,则返回 null。

我们来看下面的例子:

function gen() {
    yield 'A';
    yield 'B';
    yield 'C';

    return 'gen-return';
}

$generator = gen();

//output of the below code: object(Generator)#1 (0) { }
var_dump($generator);

// output of the below code: Fatal error
// var_dump($generator->getReturn());

// output of the below code: ABC
foreach ($generator as $letter) {
    echo $letter;
}

// string(10) "gen-return"
var_dump($generator->getReturn());

查看gen()函数定义及其return表达式,$generator变量的值可能等于gen-return字符串。然而,并非如此,因为$generator变量成为\Generator类的实例。在生成器仍然打开(未迭代)时对其调用getReturn()方法将导致致命错误。

如果代码的结构不清楚生成器是否已关闭,我们可以在获取返回值之前使用valid方法进行检查:

if ($generator->valid() === false) {
    var_dump($generator->getReturn());
}

空合并运算符

在 PHP 5 中,有一个三元运算符,它测试一个值,如果该值为true,则返回第二个元素,如果该值为false,则返回第三个元素,如下代码块所示:

$check = (5 > 3) ? 'Correct!' : 'Faulty!'; // Correct!
$check = (5 < 3) ? 'Correct!' : 'Faulty!'; // Faulty!

在使用以 web 为中心的语言(如 PHP)处理用户提供的数据时,通常会检查变量是否存在。如果变量不存在,则将其设置为某个默认值。三元运算符使我们更容易实现这一点,如下所示:

$role = isset($_GET['role']) ? $_GET['role'] : 'guest';

然而,简单并不总是快速或优雅的。有鉴于此,PHP7 通过引入 null coalesce 操作符(??,着手解决最常见的使用模式之一。

null coalesce 运算符使我们能够编写更短的表达式,如以下代码块中所示:

$role = $_GET['role'] ??'guest';

合并运算符(??被添加到$_GET['role']变量的正后方,该变量返回其第一个操作数的结果(如果它存在且不是NULL,否则返回其第二个操作数)。这意味着$_GET['role'] ?? 'guest'是完全安全的,不会引发E_NOTICE

我们还可以嵌套 coalesce 操作符:

$A = null; // or not set
$B = 10;

echo $A ?? 20; // 20
echo $A ?? $B ?? 30; // 10

从左到右读取,第一个存在且不为 null 的值是将返回的值。这种构造的好处是,它提供了一种干净有效的方法,以实现安全回退到所需的值。

提示

该书的代码包也托管在 GitHub 上的https://github.com/PacktPublishing/Modular-Programming-with-PHP7 。我们在上还提供了丰富的书籍和视频目录中的其他代码包 https://github.com/PacktPublishing/ 。看看他们!

宇宙飞船操作员

PHP7 中引入了三向比较运算符,也称为太空船运算符。其语法如下:

(expr) <=> (expr)

如果两个操作数相等,则运算符返回0,如果左侧大于,则返回1,如果右侧大于,则返回-1

它使用与其他现有比较运算符相同的比较规则:<<===>=>

operator<=> equivalent
$a < $b($a <=> $b) === -1
$a <= $b($a <=> $b) === -1 || ($a <=> $b) === 0
$a == $b($a <=> $b) === 0
$a != $b($a <=> $b) !== 0
$a >= $b($a <=> $b) === 1 || ($a <=> $b) === 0
$a > $b($a <=> $b) === 1

以下是飞船操作员行为的一些示例:

// Floats
echo 1.5 <=> 1.5; // 0
echo 1.5 <=> 2.5; // -1
echo 2.5 <=> 1.5; // 1

// Strings
echo "a"<=>"a"; // 0
echo "a"<=>"b"; // -1
echo "b"<=>"a"; // 1

echo "a"<=>"aa"; // -1
echo "zz"<=>"aa"; // 1

// Arrays
echo [] <=> []; // 0
echo [1, 2, 3] <=> [1, 2, 3]; // 0
echo [1, 2, 3] <=> []; // 1
echo [1, 2, 3] <=> [1, 2, 1]; // 1
echo [1, 2, 3] <=> [1, 2, 4]; // -1

// Objects
$a = (object) ["a" =>"b"]; 
$b = (object) ["a" =>"b"]; 
echo $a <=> $b; // 0

$a = (object) ["a" =>"b"]; 
$b = (object) ["a" =>"c"]; 
echo $a <=> $b; // -1

$a = (object) ["a" =>"c"]; 
$b = (object) ["a" =>"b"]; 
echo $a <=> $b; // 1

// only values are compared
$a = (object) ["a" =>"b"]; 
$b = (object) ["b" =>"b"]; 
echo $a <=> $b; // 0

该操作员的一个实用的用例用于编写usortuasortuksort等排序函数中使用的回调:

$letters = ['D', 'B', 'A', 'C', 'E'];

usort($letters, function($a, $b) {
return $a <=> $b;
});

var_dump($letters);

// array(5) { [0]=> string(1) "A" [1]=> string(1) "B" [2]=>string(1) "C" [3]=> string(1) "D" [4]=> string(1) "E" }

一次性的

尽管 PHP5 引入了异常模型,但总体错误和错误处理仍然有些不完善。基本上 PHP 有两个错误处理系统。传统错误仍然会突然出现,而且try…catch块无法处理。

以以下E_RECOVERABLE_ERROR为例:

class Address
{
    private $customer;
    public function __construct(Customer $customer)
    {
        $this->customer = $customer;
    }
}

$customer = new stdClass();

try {
    $address = new Address($customer);
} catch (\Exception $e) {
    echo 'handling';
} finally {
echo 'cleanup';
}

try…catch块在此无效,因为该错误不会被解释为异常,而是可捕获的致命错误:

Catchable fatal error: Argument 1 passed to Address::__construct() must be an instance of Customer, instance of stdClass given, called in script.php on line 15 and defined in script.php on line 6.

一种可能的解决方法是使用set_error_handler函数设置用户定义的错误处理程序,如下所示:

set_error_handler(function($code, $message) {
    throw new \Exception($message, $code);
});

如上所述,错误处理程序现在会将每个错误转换为异常,因此可以用try…catch块捕获它。

PHP7 将致命和可捕获的致命错误作为引擎异常的一部分,因此可以使用try…catch块捕获。这排除了仍然没有通过异常系统的警告和通知,这对于向后兼容性是有意义的。

它还通过\Throwable接口引入了一个新的异常层次结构。\Exception\Error实现\Throwable接口。

标准 PHP 致命错误和可捕获致命错误现在作为\Error异常抛出,但如果它们未捕获,则将继续触发传统致命错误。

在整个应用程序中,我们必须使用\Exception\Error,因为我们不能直接实现\Throwable接口。但是,我们可以使用以下块捕获所有错误,而不管它是\Exception还是\Error类型:

try {
// statements
} catch (\Throwable $t) {
    // handling
} finally {
// cleanup
}

解析错误

ParseError是对错误处理的一个很好的 PHP7 补充。我们现在可以处理由eval()includerequire语句触发的解析错误,以及由\ParseError异常引发的解析错误。它扩展了\Error,进而实现了\Throwable接口。

以下是由于数组项之间缺少“,”而导致 PHP 文件损坏的示例:

<?php

$config = [
'host' =>'localhost'
'user' =>'john'
];

return $config;

以下是包含config.php的文件的示例:

<?php 

try {
include 'config.php';
} catch (\ParseError $e) {
// handle broken file case
}

我们现在可以安全地捕获可能的解析错误。

对 dirname()函数的级别支持

dirname函数从 PHP4 开始就与我们在一起。它可能是 PHP 中最常用的函数之一。在 PHP 7 之前,此函数只接受path参数。在 PHP7 中,添加了新的 levels 参数。

让我们来看看下面的例子:

// would echo '/var/www/html/app/etc'
echo dirname('/var/www/html/app/etc/config/');

// would echo '/var/www/html/app/etc'
echo dirname('/var/www/html/app/etc/config.php');

// would echo '/var/www/html/app'
echo dirname('/var/www/html/app/etc/config.php', 2);

// would echo '/var/www/html'
echo dirname('/var/www/html/app/etc/config.php', 3);

通过分配levels值,我们指示从分配的路径值向上提升多少层。虽然很小,但是添加了levels参数肯定会使编写一些处理路径的代码变得更容易。

整数除法函数

intdiv是 PHP7 中引入的一个新的整数除法函数。函数接受被除数和除数作为参数,并返回除数的整数商,如下函数描述所示:

int intdiv(int $dividend, int $divisor)

让我们来看看下面的几个例子:

intdiv(5, 3); // int(1)
intdiv(-5, 3); // int(-1)
intdiv(5, -2); // int(-2)
intdiv(-5, -2); // int(2)
intdiv(PHP_INT_MAX, PHP_INT_MAX); // int(1)
intdiv(PHP_INT_MIN, PHP_INT_MIN); // int(1)

// following two throw error
intdiv(PHP_INT_MIN, -1); // ArithmeticError
intdiv(1, 0); // DivisionByZeroError

如果dividendPHP_INT_MIN且除数为-1,则抛出ArithmeticError异常。如果除数为0,则抛出DivisionByZeroError异常。

常数数组

在 PHP7 之前,define()定义的常量只能包含标量表达式,不能包含数组。从 PHP 5.6 开始,可以使用const关键字定义数组常量,从 PHP 7 开始,也可以使用define()定义数组常量:

// the define() example
define('FRAMEWORK', [
'version' => 1.2,
'licence' =>'enterprise'
]);

echo FRAMEWORK['version']; // 1.2
echo FRAMEWORK['licence']; // enterprise

// the class const example
class App {
    const FRAMEWORK = [
'version' => 1.2,
'licence' =>'enterprise'
    ];
}

echo App::FRAMEWORK['version']; // 1.2
echo App::FRAMEWORK['licence']; // enterprise

常量一旦设置,就不能重新定义或取消定义。

统一变量语法

为了使 PHP 的解析器对各种变量解引用更加完整,PHP7 引入了统一的变量语法。使用统一变量语法,所有变量都从左到右求值。

不同于正在删除的各种函数、关键字或设置,像这样的语义变化可能会对现有的代码库产生相当大的影响。下面的代码演示了语法及其旧含义和新含义:

// Syntax
$$foo['bar']['baz']
// PHP 5.x:
// Using a multidimensional array value as variable name
${$foo['bar']['baz']}
// PHP 7:
// Accessing a multidimensional array within a variable-variable
($$foo)['bar']['baz']

// Syntax
$foo->$bar['baz']
// PHP 5.x:
// Using an array value as a property name
$foo->{$bar['baz']}
// PHP 7:
// Accessing an array within a variable-property
($foo->$bar)['baz']

// Syntax
$foo->$bar['baz']()
// PHP 5.x:
// Using an array value as a method name
$foo->{$bar['baz']}()
// PHP 7:
// Calling a closure within an array in a variable-property
($foo->$bar)['baz']()

// Syntax
Foo::$bar['baz']()
// PHP 5.x:
// Using an array value as a static method name
Foo::{$bar['baz']}()
// PHP 7:
// Calling a closure within an array in a static variable
(Foo::$bar)['baz']()

除了以前重写的新旧语法示例之外,现在还有一些新支持的语法组合。

PHP7 现在支持嵌套双冒号::,下面是的一个例子:

// Access a static property on a string class name
// or object inside an array
$foo['bar']::$baz;
// Access a static property on a string class name or object
// returned by a static method call on a string class name
// or object
$foo::bar()::$baz;
// Call a static method on a string class or object returned by
// an instance method call
$foo->bar()::baz();

我们还可以通过在括号上加倍来嵌套方法和函数调用或任何可调用项,如以下代码示例所示:

// Call a callable returned by a function
foo()();
// Call a callable returned by an instance method
$foo->bar()();
// Call a callable returned by a static method
Foo::bar()();
// Call a callable return another callable
$foo()();

此外,我们现在可以取消引用任何带括号的有效表达式:

// Access an array key
(expression)['foo'];
// Access a property
(expression)->foo;
// Call a method
(expression)->foo();
// Access a static property
(expression)::$foo;
// Call a static method
(expression)::foo();
// Call a callable
(expression)();
// Access a character
(expression){0};

安全随机数发生器

PHP7 引入了两个新的CSPRNG函数。CSPRNG 是加密安全伪随机数生成器的首字母缩写。

首先,random_bytes生成适合加密使用的任意长度的加密随机字节字符串,例如在生成密钥初始化向量时。该函数只接受一个(length)参数,表示应以字节为单位返回的随机字符串的长度。它返回一个字符串,其中包含请求的加密安全随机字节数,或者,如果找不到适当的随机性源,它也会抛出异常。

以下是random_bytes用法示例:

$bytes = random_bytes(5);

第二种方法,random_int生成适合在无偏结果至关重要的情况下使用的加密随机整数,例如在扑克游戏中洗牌时。函数接受两个(minmax)参数,表示返回的最低值(必须是PHP_INT_MIN或更高)和返回的最高值(必须小于或等于PHP_INT_MAX。它返回一个加密安全的随机整数,范围从最小值到最大值(包括最小值和最大值)。

以下是random_int用法的一个例子:

$int = random_int(1, 10);
$int = random_int(PHP_INT_MIN, 500);
$int = random_int(20, PHP_INT_MAX);
$int = random_int(PHP_INT_MIN, PHP_INT_MAX);

过滤后的非序列化()

序列化的数据可以包含对象。这些对象可以进一步包括析构函数、__toString__call等函数。为了在非结构化数据上取消序列化对象时提高安全性,PHP7 在现有的unserialize函数中引入了可选的options参数。

options参数为数组类型,当前只接受allowed_classes键。

allowed_classes可以有三个值之一:

  • true:这是一个默认值,允许所有对象和以前一样
  • false:这里不允许有物体
  • 允许的类名数组,列出未序列化对象允许的类

以下是使用allowed_classes选项的示例:

class Customer{
    public function __construct(){
        echo '__construct';
    }

    public function __destruct(){
        echo '__destruct';
    }

    public function __toString(){
        echo '__toString';
        return '__toString';
    }

    public function __call($name, $arguments) {
        echo '__call';
    }
}

$customer = new Customer();

$s = serialize($customer); // triggers: __construct, __destruct

$u = unserialize($s); // triggers: __destruct
echo get_class($u); // Customer

$u = unserialize($s, ['allowed_classes'=>false]); // does not trigger anything
echo get_class($u); // __PHP_Incomplete_Class

我们可以看到,未被接受的类的对象被实例化为__PHP_Incomplete_Class

上下文敏感词条

根据至http://php.net/manual/en/reserved.keywords.php 列表,PHP 保留了 60 多个关键字。它们构成了语言结构,比如属性、方法、类中的常量、接口和特征的名称。

有时,这些保留字最终会与用户定义的 API 声明发生冲突。

为了解决这个问题,PHP7.0 引入了上下文敏感的 lexer。使用上下文敏感的 lexer,我们现在可以在代码中使用属性、函数和常量名称的关键字。

以下是一些与上下文敏感词法影响相关的实际例子:

class ReportPool {
    public function include(Report $report) {
//
    }
}

$reportPool = new ReportPool();
$reportPool->include(new Report());

class Collection extends \ArrayAccess, \Countable, \IteratorAggregate {

    public function forEach(callable $callback) {
//
    }

    public function list() {
//
    }

    public static function new(array $items) {
        return new self($items);
    }
}

Collection::new(['var1', 'var2'])
->forEach(function($index, $item){ /* ... */ })
->list();

唯一的异常是class关键字,保留在类常量上下文中,如下图:

class Customer {
  const class = 'Retail'; // Fatal error
}

集团使用声明

PHP7 中引入了组使用声明,作为从公共名称空间导入多个类时减少冗长的方法。它们支持以下速记语法:

use Library\Group1\Group2\{ ClassA, ClassB, ClassC as Classy };

让我们来看看下面的例子,其中,在使用相同的命名空间 To2 T2 的组中使用类名:

// Current use syntax
use Doctrine\Common\Collections\Expr\Comparison;
use Doctrine\Common\Collections\Expr\Value;
use Doctrine\Common\Collections\Expr\CompositeExpression;

// Group use syntax
use Doctrine\Common\Collections\Expr\{ Comparison, Value, CompositeExpression };

我们还可以在部分名称空间上使用组使用声明,如下例所示:

// Current use syntax
use Symfony\Component\Console\Helper\Table;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Output\NullOutput;
use Symfony\Component\Console\Question\Question;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ChoiceQuestion as Choice;
use Symfony\Component\Console\Question\ConfirmationQuestion;

// Group use syntax
use Symfony\Component\Console\{
  Helper\Table,
  Input\ArrayInput,
  Input\InputInterface,
  Output\NullOutput,
  Output\OutputInterface,
  Question\Question,
  Question\ChoiceQuestion as Choice,
  Question\ConfirmationQuestion,
};

我们可以进一步使用group use导入函数和常量,如下代码行所示:

use Framework\Component\{
SubComponent\ClassA,
function OtherComponent\someFunction,
const OtherComponent\SOME_CONSTANT
};

Unicode 增强

Unicode,尤其是 UTF-8,在 PHP 应用程序中越来越流行。

PHP 7 为双引号字符串heredocs添加了新的转义序列,语法如下:

\u{code-point}

它生成 Unicode 码点的 UTF-8 编码,用十六进制数字指定。值得注意的是,大括号内代码点的长度是任意的。这意味着我们可以使用\u{FF}或更传统的\u{00FF}

以下是四种交易量最大的货币及其符号和 UTF-8 代码点的简单列表:

Euro€U+20AC
Japanese Yen¥U+00A5
Pound sterling£U+00A3
Australian dollar$U+0024

其中一些符号通常直接存在于键盘上,因此很容易将它们写下来,如下所示:

echo "the € currency";
echo "the ¥ currency";
echo "the £ currency";
echo "the $ currency";

但是,大多数其他符号不像单个按键那样容易通过键盘访问,因此需要以代码点的形式编写,如下所示:

echo "the \u{1F632} face";
echo "the \u{1F609} face";
echo "the \u{1F60F} face";

在旧版本的 PHP 中,前面语句的结果输出如下:

the \u{1F632} face
the \u{1F609} face
the \u{1F60F} face

这显然并没有解析代码点,因为它是按字面意思输出它们。

PHP 7 将 Unicode 代码点转义序列语法引入字符串文本,使前面的语句产生以下输出:

the 😉 face
the 😉 face
the 😉 face
```php

## 断言

断言是一种调试功能,用于检查给定的断言,并在其结果为`false`时采取适当的操作。从 PHP4 开始,它们已经成为 PHP 的一部分很多年了。

断言与错误处理的不同之处在于断言涵盖了不可能的情况,而错误是可能的,需要处理。

应该避免使用断言作为通用错误处理机制。断言不允许从错误中恢复。断言失败通常会停止程序的执行。

对于像 Xdebug 这样的现代调试工具,没有多少开发人员使用断言进行调试。

使用`assert_options`函数或`assert.active INI`设置可以轻松启用和禁用断言。

要使用断言,我们传入表达式或字符串,如以下函数签名所示:

// PHP 5 bool assert ( mixed $assertion [, string $description ] )

// PHP 7 bool assert ( mixed $assertion [, Throwable $exception ] )


这两个签名在第二个参数中有所不同。PHP7 可以接受字符串`$description`或`$exception`。

如果表达式结果或对字符串求值的结果求值为`false`,则会发出警告。如果第二个参数作为`$exception`传递,则将引发异常而不是失败。

关于`php.ini`配置选项,`assert`功能已经扩展,允许所谓的*零成本断言*:

zend.assertions = 1 // Enable zend.assertions = 0 // Disable zend.assertions = -1 // Zero-cost


使用零成本设置,断言不会对性能和执行产生任何影响,因为它们不会被编译。

最后,将`Boolean assert.exception`选项添加到**INI**设置中。将其设置为`true`,将导致失败断言的`AssertionError`异常。

## 对 list()构造的更改

在 PHP 5 中,`list()`指定以最右边的参数开始的值。在 PHP7 中,`list()`以最左边的参数开始。基本上,值现在按照定义的顺序分配给变量。

然而,这仅影响`list()`与`array []`运算符一起使用的情况,如以下代码块中所述:

<?php

list($color1, $color2, $color3) = ['green', 'yellow', 'blue']; var_dump($color1, $color2, $color3);

list($colors[], $colors[], $colors[]) = ['green', 'yellow', 'blue']; var_dump($colors);


在 PHP 5 中输出上述代码将导致以下结果:

string(5) "green" string(6) "yellow" string(4) "blue"

array(3) { [0]=> string(5) "blue" [1]=> string(6) "yellow" [2]=> string(4) "green" }


在 PHP 7 中输出上述代码将导致以下结果:

string(5) "green" string(6) "yellow" string(4) "blue"

array(3) { [0]=> string(5) "green" [1]=> string(6) "yellow" [2]=> string(4) "blue" }


任务的顺序将来可能会再次改变,因此我们不应该过分依赖它。

## 会话选项

在 PHP7 之前的中,`session_start()`函数没有直接接受任何配置选项。我们想要在会话上设置的任何配置选项都需要来自`php.ini`:

// PHP 5 ini_set('session.name', 'THEAPP'); ini_set('session.cookie_lifetime', 3600); ini_set('session.cookie_httponly', 1); session_start();

// PHP 7 session_start([ 'name' =>'THEAPP', 'cookie_lifetime' => 3600, 'cookie_httponly' => 1 ]);


在性能优化目标的驱动下,PHP7 中添加了一个新的`lazy_write`运行时配置。当`lazy_write`设置为`1`时,仅当会话数据发生变化时才重写会话数据。这是默认行为:

session_start([ 'name' =>'THEAPP', 'cookie_lifetime' => 3600, 'cookie_httponly' => 1, 'lazy_write' => 1 ]);


虽然这里列出的更改一开始看起来可能并不令人印象深刻,但能够通过`session_start`函数直接覆盖会话选项为我们的代码提供了一定的灵活性。

## 不推荐的功能

全球接受,软件的主要版本有打破向后兼容性的奢侈。理想情况下,不会太多,但为了让软件继续向前发展,需要抛弃一些旧想法。这些变化不是一夜之间发生的。某些特性首先被标记为已弃用,以警告开发人员它将在该语言的未来版本中被删除。有时,这段时间的贬损需要几年的时间。

在整个 PHP5.x 中,许多特性都被标记为不推荐使用,在 PHP7.0 中,它们都被删除了。

与**POSIX 兼容的**正则表达式在 PHP5.3 中被弃用,现在在 PHP7 中被完全删除。

以下功能不再可用:

*   `ereg_replace`
*   `ereg`
*   `eregi_replace`
*   `eregi`
*   `split`
*   `spliti`
*   `sql_regcase`

我们应该使用而不是**Perl 兼容的正则表达式**(**PCRE**。[http://php.net/manual/en/book.pcre.php](http://php.net/manual/en/book.pcre.php) 是这些功能的重要文档来源。

PHP5.5 中不推荐使用的`mysql`扩展现在已经被删除。`mysql_*`功能不再可用。我们应该使用`mysqli`扩展名。好的是,从`mysql`到`mysqli`函数的移动非常简单,因为在代码中添加`i`时,`mysql_*`函数调用并传递数据库句柄(由`mysqli_connect`返回)作为第一个参数。[http://php.net/manual/en/book.mysqli.php](http://php.net/manual/en/book.mysqli.php) 是这些函数的重要文档来源。

PHP 脚本和 ASP 标记不再可用:

<% // Code here %> <%=$varToEcho; %>



## 框架

应用程序框架是功能、类、配置和约定的集合,所有这些都是为了支持 web 应用程序、服务和 API 的开发。一些应用程序采用 API 优先的方法,而服务器端 REST 和 SOAP API 是通过 PHP 构建的,而客户端则采用其他技术,如 JavaScript。

在构建 web 应用程序时,我们通常有三个明显的选择:

*   我们可以自己建造一切,从头开始。这样,我们的开发过程可能会很慢,但我们可以实现完全按照我们的标准构建的体系结构。不用说,这是一种非常低效的方法。
*   我们可以使用现有的框架之一。这样,我们的开发过程很快,但我们需要高兴的是,我们的应用程序是建立在其他东西之上的。
*   我们可以使用现有的框架之一,但也可以尝试将其抽象到应用程序看起来独立于它的级别。至少可以说,这是一种痛苦而缓慢的做法。它涉及编写大量适配器、包装器、接口等。

简言之,框架是为了让我们更容易、更快地构建软件。很多编程语言都有流行的框架。PHP 也不例外。

鉴于 PHP 作为一种流行的 web 编程语言,多年来涌现出几十种框架也就不足为奇了。选择“正确”的框架是一项艰巨的任务,对新手来说更是如此。适用于一个项目或团队的内容可能不适用于另一个项目或团队。

然而,每个现代框架都应该包含一些一般性的高级别部分。这些原因包括:

*   **模块化**:它支持模块化应用开发,允许我们将代码整齐地划分为功能构建块,而它是以模块化方式构建的。
*   **安全**:它提供了现代 web 应用所需的各种加密和其他安全工具。为身份验证、授权和数据加密等提供无缝支持。
*   **可扩展**:管理轻松采用我们的应用需求,允许我们根据应用需求进行扩展。
*   **社区**:由一个充满活力和活力的社区积极发展和支持。
*   **高性能**:在中考虑性能。许多框架吹嘘性能,但其中有许多变量。我们需要明确我们在这里评估的内容。根据原始性能衡量缓存性能通常是一种误导性的评估,因为缓存代理可以放在许多框架前面。
*   **企业就绪**:根据手头的项目类型,我们很可能希望针对一个将自身标记为企业就绪的框架。使我们有足够的信心在 it 之上运行关键的、高使用率的业务应用程序。

虽然不用任何框架就可以用纯 PHP 编写整个 web 应用程序,但今天的大多数项目都使用框架。

使用框架的好处超过了从头开始的纯粹性。框架通常得到很好的支持和文档化,这使得团队更容易赶上库、项目结构、约定和其他方面。

谈到 PHP 框架,值得指出几个流行的框架:

*   **拉维**:[https://laravel.com](https://laravel.com)
*   **Symfony**:[http://symfony.com](http://symfony.com)
*   **Zend 框架**:[http://framework.zend.com](http://framework.zend.com)
*   **代码点火器**:[https://www.codeigniter.com](https://www.codeigniter.com)
*   **CakePHP**:[http://cakephp.org](http://cakephp.org)
*   **苗条**:[http://www.slimframework.com](http://www.slimframework.com)
*   **易**:[http://www.yiiframework.com](http://www.yiiframework.com)
*   **法尔康**:[https://phalconphp.com](https://phalconphp.com)

这并不是一个完整的,甚至不是一个流行的排序列表。

### 拉威尔框架

Laravel 是根据麻省理工学院许可证发布的,可以从[下载 https://laravel.com/](https://laravel.com/) 。

除了通常的路由、控制器、请求、响应、视图和(刀片式)模板之外,现成的 Laravel 还提供大量附加服务,如身份验证、缓存、事件、本地化和许多其他服务。

Laravel 的另一个整洁的特性是**Artisan**,命令行工具,它提供了许多有用的命令,我们可以在开发过程中使用这些命令。Artisan 可以通过编写自己的控制台命令来进一步扩展。

拉威尔有一个相当活跃和充满活力的社区。它的文档简单明了,这使得新手很容易入门。此外,还有[https://laracasts.com](https://laracasts.com) ,在文档和其他内容方面超出了 Laravel。Laracasts 是一个 web 服务,提供一系列专家屏幕广播,其中一些是免费的。

在选择框架时,所有这些特性使 Laravel 成为一个值得评估的选择。

### Symfony

Symfony 是根据麻省理工学院许可证发布的,并且可以从[下载 http://symfony.com](http://symfony.com) 。

随着时间的推移,Symfony 引入了**长期支持**(**LTS**版本)的概念。本发布流程自 Symfony 2.2 起采用,自 Symfony 2.4 起严格遵循。Symfony 的标准版本维持八个月。长期支持版本支持三年。

关于新发布的另一件有趣的事情是基于时间的发布模型。Symfony 的所有新版本每六个月发布一次:一个在五月,一个在十一月。

Symfony 通过邮件列表、IRC 和 StackOverflow 获得了强大的社区支持。此外,SensioLabs 专业支持提供从咨询、培训、指导到认证的全方位解决方案。

许多 Symfony 组件用于其他 web 应用程序和框架,如 Laravel、Silex、drupal8、Sylius 等。

Symfony 之所以成为如此流行的框架,是因为它的互操作性。“不要把自己锁在 Symfony 中!”的想法使它受到开发人员的欢迎,因为它允许构建精确满足我们需求的应用程序。

Symfony 秉承“不要重新发明轮子”的理念,大量使用现有的 PHP 开源项目作为框架的一部分,包括:

*   条令(或推进):对象关系映射层
*   PDO 数据库抽象层(条令或推进)
*   PHPUnit:一个单元测试框架
*   小树枝:模板引擎
*   swiftmailer:一个电子邮件库

根据我们的项目需要,我们可以选择使用全堆栈 Symfony 框架、Silex micro 框架,或者单独使用一些组件。

Symfony 为新的 web 应用程序提供了大量现成的结构基础。它通过其捆绑系统实现这一点。捆绑包有点像主应用程序中的微应用程序。在它们内部,整个应用程序被很好地组织成模型、控制器、模板、配置文件和其他构建块。能够从不同领域完全分离逻辑有助于我们保持关注点的干净分离,并自主开发我们领域的每一个特性。

Symfony 是 PHP 的先驱之一,它支持跨框架的依赖项注入,允许它实现解耦组件并保持代码的高度灵活性。

文档化、模块化、高度灵活、性能优异、受支持,这些特性使 Symfony 成为值得评估的选择。

### Zend 框架

Zend 框架以新的 BSD 许可证发布,可从[下载 http://framework.zend.com](http://framework.zend.com) 。

Zend Framework 功能包括:

*   完全面向对象的 PHP 组件
*   松耦合组件
*   支持布局和模板的可扩展 MVC
*   支持多个数据库系统 MySQL、Oracle、MS SQL 等
*   通过 mbox、Maildir、POP3 和 IMAP4 处理电子邮件
*   灵活的缓存系统

除了免费的 Zend 框架之外,Zend Technologies Ltd 还提供了自己的商业版 PHP 堆栈 Zend Server 和 Zend Studio IDE,其中包括专门与 Zend 框架集成的功能。虽然 Zend Framework 在任何 PHP 堆栈上都可以很好地运行,但 Zend Server 被宣传为运行 Zend Framework 应用程序的优化解决方案。

根据其架构设计,Zend 框架只是一个类的集合。我们的应用程序不需要遵循严格的结构。这是一个特性,使得它对一定范围的开发人员如此有吸引力。我们可以利用 Zend MVC 组件创建一个功能齐全的 Zend Framework 项目,也可以简单地加载所需的组件。

所谓的全堆栈框架将结构、ORM 实现、代码生成和其他固定内容强加给项目。另一方面,Zend 框架由于其解耦的特性,将其分类为粘合类型的框架。我们可以很容易地将它粘贴到现有的应用程序上,或者使用它来构建一个新的应用程序。

Zend Framework 的最新版本遵循**实体面向对象设计**原则。所谓的“随意使用”设计允许开发人员使用他们想要的任何组件。

虽然 Zend 框架背后的主要驱动力是 Zend Technologies,但许多其他公司也为该框架提供了重要功能。

此外,Zend Technologies 还提供优秀的 Zend 认证 PHP 工程师认证。优质社区、官方公司支持、教育、托管和开发工具使 Zend 框架选择值得评估。

### 编码点火器

CodeIgniter 是根据麻省理工学院许可证发布的,可以从[下载 https://www.codeigniter.com](https://www.codeigniter.com) 。

CodeIgniter 以重量轻而自豪。核心系统只需要少数几个小型库,其他框架并不总是如此。

框架使用简单的**模型视图控制**方法,允许逻辑和表示之间的清晰分离。视图层不强制使用任何特殊的模板语言,因此它使用本机 PHP 开箱即用。

以下是 CodeIgniter 的一些突出特点:

*   基于模型-视图控制的系统
*   重量极轻
*   功能齐全的数据库类,支持多种平台
*   查询生成器数据库支持
*   表单和数据验证
*   安全性和 XSS 过滤
*   本地化
*   数据加密
*   整页缓存
*   单元测试类
*   搜索引擎友好的 URL
*   灵活的 URI 路由
*   对钩子和类扩展的支持
*   大型辅助函数库

CodeIgniter 在[周围聚集了一个活跃的社区 http://forum.codeigniter.com](http://forum.codeigniter.com) 。

占地面积小、灵活性好、性能优异、接近零的配置和完整的文档是值得评估的框架选择。

### CakePHP

CakePHP 是根据 MIT 许可证发布的,并且可以从[下载 http://cakephp.org](http://cakephp.org) 。

CakePHP 框架深受**RubyonRails**的启发,使用了许多概念。它重视约定而不是配置。

它附带了“电池”。现代 web 应用程序所需的大多数东西都是内置的。翻译、数据库访问、缓存、验证、身份验证等等都是内置的。

安全性是 CakePHP 哲学的另一个重要部分。CakePHP 提供了用于输入验证、CSRF 保护、表单篡改保护、SQL 注入预防和 XSS 预防的内置工具,帮助我们保护应用程序的安全。

CakePHP 支持多种数据库存储引擎,如 MySQL、PostgreSQL、Microsoft SQL Server 和 SQLite。内置的 CRUD 特性对于数据库交互非常方便。

它依靠一个大社区来支持它。它还有一个很大的插件列表,可以在[上找到 http://plugins.cakephp.org](http://plugins.cakephp.org) 。

CakePHP 提供了一个认证考试,通过该考试,开发人员可以测试他们对 CakePHP 框架、MVC 原则和 CakePHP 中使用的标准的知识。认证面向现实世界场景和亲密的 CakePHP 细节。

Cake development Corporation[提供商业支持、咨询、代码审查、性能分析、安全审计,甚至开发服务 http://www.cakedc.com](http://www.cakedc.com) 。Cake Development Corporation 是框架背后的商业实体,由 CakePHP 创始人 Larry Masters 于 2007 年成立。

### 苗条

Slim 根据 MIT 许可证发布,可从[下载 http://www.slimframework.com](http://www.slimframework.com) 。

虽然具有“包括电池”思维的框架提供了健壮的库、目录结构和配置,但微框架让我们从几行代码开始。

微框架通常甚至缺少基本框架功能,例如:

*   认证和授权
*   ORM 数据库抽象
*   输入验证和卫生
*   模板引擎

这限制了它们的使用,但也使它们成为快速原型制作的伟大工具。

Slim 支持任何 PSR-7 HTTP 消息实现。HTTP 消息是从客户端到服务器的请求或从服务器到客户端的响应。Slim 的功能类似于接收 HTTP 请求、调用适当的回调例程并返回 HTTP 响应的调度器。

Slim 的好处在于它可以很好地使用中间件。中间件基本上是可调用的,它接受三个参数:

*   `\Psr\Http\Message\ServerRequestInterface`:PSR7 请求对象
*   `\Psr\Http\Message\ResponseInterface`:PSR7 响应对象
*   `callable`:下一个中间件可调用

中间件可以自由操作请求和响应对象,只要它们返回一个`\Psr\Http\Message\ResponseInterface`实例。此外,每个中间件都需要调用下一个中间件,并将其作为参数传递给请求和响应对象。

这个简单的概念通过各种可能的第三方中间软件赋予 Slim 扩展能力。

尽管 Slim 提供了良好的文档、一个充满活力的社区,并且到目前为止该项目正在积极开发中,但它的使用是有限的。对于健壮的企业应用程序来说,微型框架几乎不是一种选择。尽管如此,它们在发展中仍占有一席之地。

### 易

Yii 根据 BSD 许可证发布,可从[下载 http://www.yiiframework.com](http://www.yiiframework.com) 。

Yii 对性能优化的关注使其成为几乎任何类型项目的完美选择,包括企业类型的应用程序。

一些突出的 Yii 功能包括:

*   MVC 设计模式
*   复杂服务 WSDL 的自动生成
*   日期、时间和数字的翻译、本地化、区域设置相关格式
*   数据缓存、片段缓存、页面缓存和 HTTP 缓存
*   错误处理程序,根据错误的性质和应用程序运行的模式显示错误
*   有助于防止 SQL 注入、**跨站点脚本编制**(**XSS**)、**跨站点请求伪造**(**CSRF**)和 cookie 篡改的安全措施
*   基于**PHPUnit**和**Selenium**的单元和功能测试

Yii 的整洁特征之一是一个名为**Gii**的工具。它是一个扩展,提供了一个基于 web 的代码生成器。我们可以使用 Gii 的图形界面快速设置生成模型、表单、模块、CRUD 等。对于那些喜欢控制台而不是 GUI 的人来说,Gii 还有一个命令行版本。

Yii 的体系结构允许它与第三方代码(如 PEAR 库、Zend 框架等)配合使用。它采用 MVC 架构,允许清晰地分离关注点。

Yii 在[提供了一个令人印象深刻的扩展库 http://www.yiiframework.com/extensions](http://www.yiiframework.com/extensions) 。大多数扩展都作为 composer 包分发。它们使我们能够加速发展。我们可以轻松地将代码打包为扩展并与其他人共享。这使得 Yii 对于模块化应用程序开发更加有趣。

官方文件相当全面。还有几本书可供选择。

丰富的文档、充满活力的社区、活跃的版本、性能优化、强调安全性、功能丰富性和灵活性使 Yii 成为值得评估的选择。

### 法尔康

Phalcon 根据 BSD 许可证发布,可从[下载 https://phalconphp.com](https://phalconphp.com) 。

Phalcon 最初由 Andres Gutierrez 及其合作者于 2012 年发布。项目的目标是为用 PHP 编写的传统 web 应用程序框架找到一种新方法。这种新方法以 C 语言扩展的形式出现。整个 Phalcon 框架是作为 C 扩展开发的。

基于 C 的框架的好处在于,整个 PHP 扩展都是在运行时加载的。这大大减少了 I/O 操作,因为不再需要加载`.php`文件。此外,编译的 C 语言代码执行速度比 PHP 字节码快。由于在 web 服务器守护进程启动过程中,C 扩展与 PHP 一起加载一次,因此它们的内存占用很小。基于 C 的框架的缺点是代码是经过编译的,因此我们不能像使用 PHP 类那样轻松地调试和修补代码。

低层次的架构和优化使 Phalcon 成为基于 MVC 的应用程序的最低开销之一。

Phalcon 是一个完整的、松散耦合的框架。虽然它确实为我们的应用程序提供了完整的 MVC 结构,但它也允许我们根据应用程序的需要将其对象用作粘合组件。我们可以选择是创建一个成熟的 MVC 应用程序,还是创建一个简约风格的微应用程序。微型应用程序适合以实用的方式实现小型应用程序、API 和原型。

到目前为止,我们提到的所有框架都支持某种形式的扩展,我们可以在其中向框架添加新的库或整个包。由于 Phalcon 是一个 C 代码框架,因此对该框架的贡献不是以 PHP 代码的形式出现的。另一方面,编写和编译 C 语言代码对普通 PHP 开发人员来说可能有些挑战。

**泽菲尔**项目[http://zephir-lang.com](http://zephir-lang.com) 通过引入高级 Zephir 语言来应对这些挑战。Zephir 旨在简化 PHP C 扩展的创建和可维护性,重点关注类型和内存安全。

当与数据库通信时,Phalcon 使用**Phalcon 查询语言**、**PhalconQL**或简称**PHQL**。PHQL 是一种高级的、面向对象的 SQL 方言,它允许我们使用类似 SQL 的语言编写查询,这种语言处理对象而不是表。

视图模板由 Volt 处理,Volt 是 Phalcon 自己的模板引擎。它与其他组件高度集成,可以在我们的应用程序中独立使用。

法尔康很容易学。它的文档涵盖了使用框架的 MVC 和微应用程序风格,并提供了实际示例。该框架本身非常丰富,足以支持当今大多数应用程序所需的结构和库。除此之外,还有一个名为**Phelconist**[的 Phelcon 官方网站 https://phalconist.com](https://phalconist.com) 为框架提供额外的资源。

虽然没有官方公司支持,没有认证,没有商业支持,以及类似的企业外观,但 Phalcon 在将自己定位为一个值得评估的选择方面做了大量工作,即使有强大的企业应用程序开发。

# 总结

回顾 PHP5 的发布及其对 OOP 编程的支持,我们可以看到它对 PHP 生态系统的巨大积极影响。大量的框架和库已经扩展开来,为 web 应用程序开发提供了企业级的解决方案。

PHP7 的发布可能是 PHP 生态系统的又一次飞跃。尽管这些新特性本身都不是革命性的,因为它们可以在多年前的其他编程语言中找到,但它们对 PHP 的影响很大。我们还没有看到它的新特性将如何重塑现有和未来的框架以及我们编写应用程序的方式。

将更高级的*错误引入异常*处理、标量类型提示和函数返回类型提示,必将为使用它们的应用程序和框架带来人们期待已久的稳定性。与 PHP5.6 相比,速度的提高非常显著,足以降低高负载站点的托管成本。谢天谢地,PHP 开发团队尽量减少了向后不可比性的更改,因此它们不应该妨碍快速采用 PHP7。

选择正确的框架几乎是一项简单的任务。将框架分类为企业级框架的不仅仅是类的集合。它周围有一个完整的生态系统。

在评估一个项目的框架时,千万不要被炒作所驱使。应考虑以下问题:

*   是公司驱动还是社区驱动?
*   它是否提供高质量的文档?
*   它是否有一个稳定且频繁的发布周期?
*   它是否提供某种官方形式的认证?
*   它是否提供免费和商业支持?
*   有没有我们偶尔可以参加的研讨会?
*   它是否对社区参与开放,以便我们可以提交功能和补丁?
*   它是一个完整的堆栈还是胶水类型的框架?
*   是约定驱动还是配置驱动?
*   它是否提供足够的库让您开始(安全性、验证、模板、数据库抽象、ORMs、路由、国际化等等)?
*   核心框架是否能够得到足够的扩展和覆盖,以使其在可能的更改中更加经得起未来的考验?

有许多已建立的 PHP 框架和库,因此选择非常简单。这些框架和库中的大多数仍然没有完全跟上 PHP7 中添加的最新特性。

接下来,在下一章中,我们将研究常见的设计模式以及如何将它们集成到 PHP 中。

教程来源于Github,感谢apachecn大佬的无私奉献,致敬!

技术教程推荐

硅谷产品实战36讲 -〔曲晓音〕

深入剖析Kubernetes -〔张磊〕

白话法律42讲 -〔周甲徳〕

OpenResty从入门到实战 -〔温铭〕

编译原理之美 -〔宫文学〕

微信小程序全栈开发实战 -〔李艺〕

分布式金融架构课 -〔任杰〕

李智慧 · 高并发架构实战课 -〔李智慧〕

深入浅出可观测性 -〔翁一磊〕