PHP4的时代为面向过程的时代,PHP5引入了类,PHP从面向过程转为了面向对象,PHP5.3引入了命令空间,此后PHP不断有新的特性使被添加,composer依赖管理工具的诞生,PHP社区制定了PSR规范等,存在诸如Symfony、Mongolog、SwiftMailer等一系列高可用组件,使得PHP可轻松开发和组织大型工程项目。这与现代PHP的新特性密不可分。以下介绍部分常用的现代特性。
命名空间
命名空间是在PHP5.3.0中引入,作用是按照一种虚拟层次结构组织代码,其层次结构类似操作系统中的文件系统的目录结构。现在的PHP组件和框架都放置在各自全局唯一的厂商命名空间中,以避免常见命名冲突。
例如: Symfony 框架中的 symfony/httpfoundation 组件
创建命名空间
1 2 3 4 5 6 7
| <?php namespace Symfony\Component\HttpFoundation;
class Response {
}
|
命名空间的作用是封装和组织相关的PHP类,如同文件系统中将相关的文件放在同一个目录中管理一样。
命名空间是一个虚拟的概念,与操作系统的物理文件系统不同,没必要和文件系统中的目录结构完全对应。虽然如此,多数PHP组件为了兼容,广泛使用 PSR-4 自动加载器标准,会将子命名空间放到文件系统的子目录中。
从技术层面上看,命名空间只是PHP语言中的一种记号,PHP解释器会将其作为前缀添加到类、接口、函数、常亮名称的前面。
使用命名空间中的类、函数、常量及使用别名
1 2 3 4 5
| <?php use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response as Res; use function GuzzleHttp\json_encode; use constant Namespace\CONST_NAME;
|
自动加载
命名空间还为PHP-FIG制定的PSR-4自动加载标准奠定了坚实的基础,大多数现代的PHP组件都使用了这种自动加载模式,使用依赖管理器Composer可以自动加载项目的依赖。
使用接口
接口是两个PHP对象之间的契约(Contract),其目的不是让一个对象依赖另一个对象的身份,而是依赖另一个对象的能力。接口把代码和依赖解耦了,而且允许代码依赖任何实现了预期接口的第三方代码。
定义DocumentStore类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
| <?php
class DocumentStore { protected $data = [];
public function addDocumet(Documentable $document) { $key = $document->getId(); $value = $document->getContent(); $this->data[$key] = $value; }
public function getDocuments() { return $this->data; } }
interface Documentable { public function getId();
public function getContent(); }
class HtmlDocument implements Documentable { protected $url;
public function __construct($url) { $this->url = $url; }
public function getId() { return $this->url; }
public function getContent() { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $this->url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 3); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); curl_setopt($ch, CURLOPT_MAXREDIRS, 3); $html = curl_exec($ch); curl_close($ch);
return $html; } }
class StreamDocument implements Documentable { protected $resource;
protected $buffer;
public function __construct($resource, $buffer = 4096) { $this->resource = $resource; $this->buffer = $buffer; }
public function getId() { return 'resource-' . (int)$this->resource; }
public function getContent() { $streamContent = ''; rewind($this->resource); while (feof($this->resource) === false) { $streamContent .= fread($this->resource, $this->buffer); }
return $streamContent; } }
class CommandOutputDocument implements Documentable { protected $command;
public function __construct($command) { $this->command = $command; }
public function getId() { return $this->command; }
public function getContent() { return shell_exec($this->command); } }
$documentStore = new DocumentStore();
$htmlDoc = new HtmlDocument('http://php.net'); $documentStore->addDocumet($htmlDoc);
$streamDoc = new StreamDocument(fopen('stream.txt', 'rb')); $documentStore->addDocumet($streamDoc);
$cmdDoc = new CommandOutputDocument('cat /etc/hosts'); $documentStore->addDocumet($cmdDoc);
print_r($documentStore->getDocuments());
|
性状(Trait)
性状是类的部分实现(即常量,属性和方法),可以混入一个或多个现有的PHP类中。性状有两个作用:表明类可以做什么(像是接口);提供模块化实现(像是类)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <?php trait Hello { public function sayHelloWorld() { echo 'Hello'.$this->getWorld(); } abstract public function getWorld(); }
class MyHelloWorld { private $world; use Hello; public function getWorld() { return $this->world; } public function setWorld($val) { $this->world = $val; } }
|
生成器(Generator)
生成器会根据需求计算并产出要迭代的值。使用生成器即时算出并产出后续的值,不占用宝贵的内存资源。
1 2 3 4 5 6 7 8 9 10 11
| <?php function xrange($start, $end) { for ($i = $start; $i < $end; ++$i) { yield $i; } }
foreach (xrange(1, 1000) as $value){ echo $value, PHP_EOL; }
|
yield 每次返回一个Generator实例,foreach可遍历Iterator迭代器。
闭包(Closure)
闭包是指在创建时封装周围状态的函数。即便闭包所在的环境不在了,闭包中封装的状态依然存在。
匿名函数其实就是没有名称的函数。匿名函数可以赋值给变量,还能像其他任何PHP对象那样传递。不过匿名函数仍是函数,因此可以调用,还可以传入参数。匿名函数特别适合作为函数或方法的回调。
闭包和匿名函数实际上是Closure类的实例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| class App { protected $routes = []; protected $responseStatus = '200 OK'; protected $resposneContentType = 'text/html'; protected $responseBody = 'Hello world';
public function addRoute($routePath, Closure $routeCallback){ $this->routes[$routePath] = $routeCallback->bindTo($this, __CLASS__); }
public function dispatch($currentPath){ foreach ($this->routes as $routePath => $callback){ if($routePath === $currentPath){ $callback(); } }
header('HTTP/1.1 '.$this->responseStatus); header('Content-Type: '.$this->resposneContentType); header('Content-Length: '.mb_strlen($this->responseBody)); echo $this->responseBody; } }
$app = new App(); $app->addRoute('/users/josh', function(){ $this->responseContentType = 'application/json; charset=utf-8'; $this->responseBody = '{"name": "Josh"}'; }); $app->dispatch($_SERVER['REQUEST_URI']);
|