現代PHP読書メモ1
PHPについて誤解が多いですが、実は現代PHPは開発効率、実行効率が非常に高いプログラミング言語です。最新の PHP のさまざまな機能については、<modern php></modern>
著者が以前に書いた 正しい方法での PHP、中国語訳: 正しい方法での PHP を参照してください。同時に、著者は人気のある PHP フレームワークである Slim の開発者でもあります。したがって、この本は、OOP の概念をいくつか理解するだけでよく、PHP 開発について知る必要はありません。
PHP 名前空間は、合計名前空間を区切るために "" 文字を使用します。オペレーティング システムの物理ファイル システムとは異なり、PHP 名前空間は抽象概念であり、ファイル ディレクトリと 1 対 1 に対応する必要はありません。ほとんどの PHP コンポーネントは、PSR-4 オートローダー標準に従って、サブ名前空間とファイル ディレクトリ間のマッピングを編成します。
技術的に言えば、名前空間は単なる PHP 言語のシンボルであり、PHP インタープリターはこのシンボルをクラス/インターフェイス/関数/定数のセットの接頭辞として使用するだけです。
Namespaces are important because they let us create sandboxed code that works alongside other developer's code. This is the cornerstone concept of the modern PHP component ecosystem.
1. 複数のインポート
悪い:
<code class="language-php?linenums hljs tex"><?phpuse Symfony<span class="hljs-command">\Component</span><span class="hljs-command">\HttpFoundation</span><span class="hljs-command">\Request</span>, Symfony<span class="hljs-command">\Component</span><span class="hljs-command">\HttpFoundation</span><span class="hljs-command">\Response</span>, Symfony<span class="hljs-command">\Component</span><span class="hljs-command">\HttpFoundation</span><span class="hljs-command">\Cookie</span>;</code>
良い:
<code class="language-php?linenums hljs tex"><?phpuse Symfony<span class="hljs-command">\Component</span><span class="hljs-command">\HttpFoundation</span><span class="hljs-command">\Request</span>; use Symfony<span class="hljs-command">\Component</span><span class="hljs-command">\HttpFoundation</span><span class="hljs-command">\Response</span>; use Symfony<span class="hljs-command">\Component</span><span class="hljs-command">\HttpFoundation</span><span class="hljs-command">\Cookie</span>;</code>
2. ファイルごとに 1 つのクラス
3. グローバル名前空間
名前空間のないクラス/インターフェイスを参照する場合/function/constant、PHP はまず、この class/interface/function/constant が現在の名前空間にあると想定します。 PHP は、現在の名前空間で見つからない場合にのみ解決を開始します。名前空間を持たないコードの場合、PHP はそのコードがグローバル名前空間に存在するとみなします。
PSR-4
An interface is a contract between tow PHP objects that lets one object depend not on what another object is but, instead, on what another can do.
A trait is a partial class implementation(i.e., constants, properties, and methods) that can be mixed into one or more existing PHP classes. Traits work double duty: they say what a class can do (like an interface), and they provide a modular implementation (like class).
iOS の開発と比較して、私のお気に入りの機能の 1 つは、PHP の特性はカテゴリに似ていますが、それでも異なります。
1. OC は特定のクラスでのみ実行できます。 、PHP のトレイトは無関係なクラスにコード ユニットを挿入できます。同時に、OC のカテゴリは属性の拡張を直接実装できませんが、PHP のトレイトは定数、属性、およびメソッドを実装できます。 3. PHP トレイトと OC カテゴリは根本的に目的が異なります。OC は既存のクラスを直接拡張するものであり、実装クラスを継承する必要はありません。 PHP の特性は、クラス定義で
を使用して指定する必要があります。 use
は、
のクラスおよびインターフェースの定義と同じです。 ジェネレーター
Generators are easy to create because they are just PHP functions that use the yield keyword one or more times. Unlike regular PHP functions, generators never return a value. They only yield values.
この概念は、Swift を含む Python にこの機能があり、大量のデータを反復して動的にデータを取得するために使用できます。メモリの無駄を避けるために、一度にすべてを生成するのではなく。
各反復中に、PHP はジェネレーター インスタンスに次の反復値を計算して提供するように要求します。このプロセスでは、ジェネレーターが降伏値に達すると、ジェネレーターは内部状態の実行を一時停止します。ジェネレーターが次の反復値を提供するように要求された場合にのみ、ジェネレーターは内部状態の実行を継続します。ジェネレーターはこのように一時停止と再開を繰り返し、ジェネレーターの関数定義の最後に到達するか空になるまで実行を終了しません。
A closure is a function that encapsulates its surrounding state at the time it is created. The encapsulated state exists inside the closure even when the closure lives after it original environment ceases to exist.
这里的闭包是指Closure和Anonymous functions。上面是作者对于闭包的解释,感觉非常准确,比我看到的大多数解释都要简单清晰。闭包在日常业务开发中非常有用,可以非常方便替换我们经常需要用到的delegate设计模式,不需要再去定义一个interface,然后再实现这个interface,再把对应的对象指针传递过去。而是通过Closure,只需要简简单单传递一段代码即可,这个极大简化了日常业务开发。所以目前iOS开发中,大家通常都会使用block来代替delegate设计模式。
PHP Closure or Anonymous function 跟PHP function的定义语法是一样的,但是实际上 Closure 的背后是Closure class的实例,所以Closure被认为是first-class value types。
Attach State : PHP的Closure不会automatically enclose application state,不像JavaScript/OC那样会capture作用域之外的变量。而是,you must manually attach state to a PHP closure with the closure object's bindTo() method or the use keyword.
需要注意的是,PHP closures是objects。而我们之所以能让一个closure 实例变量进行调用,是因为这个对象实现 __invoke()
magic method,当我们在closure实例变量后面跟着一个 ()
的时候,closure实例变量就会寻找并且调用__invoke()
方法,例如 $closure("Jason")
。
同样,由于PHP closure是objects。所以,在closure内部我们也可以通过 $this
访问closure的各种内部状态,但是这个状态是非常boring。同时,closure的bindTo()
方法可以有一些非常有趣的特殊用法,This method lets us bind a Closure object's internal state to a different object. The bindTo() method accepts an important second argument that specifies the PHP class of the object to which the closure is bound.This lets the closure access protected and private member variables of the object to which it is bound.
。这个用法有点类似JavaScript的bind方法,可以改变Closure object的 $this 指针指向。
bindTo()这个有趣的用法,经常各种PHP框架的路由所采用,例如:
<code class="language-php?linenums hljs xml"><span class="php"><span class="hljs-preprocessor"><?</span> php<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">App</span>{</span> <span class="hljs-keyword">protected</span> <span class="hljs-variable">$routes</span> = <span class="hljs-keyword">array</span>(); <span class="hljs-keyword">protected</span> <span class="hljs-variable">$responseStatus</span> = <span class="hljs-string">'200 OK'</span>; <span class="hljs-keyword">protected</span> <span class="hljs-variable">$responseContentType</span> =<span class="hljs-string">'text/html'</span>; <span class="hljs-keyword">protected</span> <span class="hljs-variable">$responseBody</span> = <span class="hljs-string">'Hello world'</span>; <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">addRoute</span><span class="hljs-params">(<span class="hljs-variable">$routePath</span>, <span class="hljs-variable">$routeCallback</span>)</span> {</span> <span class="hljs-comment">// 将Closure bind到App类上</span> <span class="hljs-variable">$this</span>->routes[<span class="hljs-variable">$routePath</span>] = <span class="hljs-variable">$routeCallback</span>->bindTo(<span class="hljs-variable">$this</span>, <span class="hljs-keyword">__CLASS__</span>); } <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">dispatch</span><span class="hljs-params">(<span class="hljs-variable">$currentPath</span>)</span> {</span> <span class="hljs-keyword">foreach</span> (<span class="hljs-variable">$this</span>->routes <span class="hljs-keyword">as</span> <span class="hljs-variable">$routePath</span> => <span class="hljs-variable">$callback</span>) { <span class="hljs-keyword">if</span> (<span class="hljs-variable">$routePath</span> === <span class="hljs-variable">$currentPath</span>) { <span class="hljs-variable">$callback</span>(); } } <span class="hljs-comment">// 这里返回的state是在callback内修改过的</span> header(<span class="hljs-string">'HTTP/1.1 '</span>.<span class="hljs-variable">$this</span>.responseStatus); header(<span class="hljs-string">'Content-type: '</span>.<span class="hljs-variable">$this</span>.responseContentType); header(<span class="hljs-string">'Content-length: '</span>.mb_strlen(<span class="hljs-variable">$this</span>->responseBody)); <span class="hljs-keyword">echo</span> <span class="hljs-variable">$this</span>->responseBody; }}<span class="hljs-comment">// 添加注册一个路由</span><span class="hljs-preprocessor"><?php</span><span class="hljs-variable">$app</span> = <span class="hljs-keyword">new</span> App();<span class="hljs-variable">$app</span>->addRoute(<span class="hljs-string">'/users/josh'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span><span class="hljs-params">()</span> {</span> <span class="hljs-comment">// 因为这个route是bindTo到App class上的,所以这里直接访问$this修改 App 的内部state</span> <span class="hljs-variable">$this</span>->responseContentType = <span class="hljs-string">'application/json;charset=utf8'</span>; <span class="hljs-variable">$this</span>->responseBody = <span class="hljs-string">'{"name": "Josh"}'</span>;});<span class="hljs-variable">$app</span>->dispatch(<span class="hljs-string">'/users/josh'</span>);</span></code>
从PHP 5.5.0开始,PHP引入了内置的bytecode cache支持,叫做 Zend OPcache
。PHP解释器在执行PHP脚本的时候,会首先把PHP代码编译为Zend Opcodes (machine-code instructions),然后才会执行bytecode。在所有请求中,PHP解释器都需要这样处理所有的PHP文件,read/parse/compiles,然而我们可以通过把PHP文件预编为PHP bytecode来省略这个开销,这就是Zend OPcache
。
Zend OPcache
的使用非常简单,在我们配置之后,它就会在内存中自动缓存precompiled PHP bytecode,在可用的情况就会直接执行这个PHP bytecode,而不需要再去编译PHP代码。
具体配置去google吧,有一点需要注意的是,如果同时配置了 Xdebug的话,在php.ini文件中,需要在Xdebug之前加载Zend OPcache extension扩展。
PHP从5.4.0引入了内置的HTTP server,所以我们在不配置Apache或者nginx的情况下就直接预览PHP程序。
<br>Remember, the PHP built-in server is a web server. It speaks HTTP, and it can serve static assets in addition to PHP files. It's a great way to write and preview HTML locally without installing MAMP, WAMP, or a heavyweight web server. <br>
要使用内置的HTTP server非常简单,在工程的根目录下,执行下面的命令即可:
<code class="language-bash hljs ">php -S localhost:<span class="hljs-number">4000</span></code>
如果要让本地网络的其他设备访问PHP web server,我们可以这么启动:
<code class="language-bash hljs ">php -S <span class="hljs-number">0.0</span>.<span class="hljs-number">0.0</span>:<span class="hljs-number">4000</span></code>
如果我们希望通过 PHP INI 配置文件去做一些特殊配置,可以通过下面命令来启动:
<code class="language-bash hljs ">php -S localhost:<span class="hljs-number">8000</span> -c app/config/php.ini</code>
我们也可以通过Router Scripts来实现一些特殊的路由需求,可以通过下面的命令启动:
<code class="language-bash hljs ">php -S localhost:<span class="hljs-number">8000</span> router.php</code>
在PHP代码中,我们可以通过php_sapi_name()
来判断:
<code class="language-php?linenums hljs xml"><span class="php"><span class="hljs-preprocessor"><?php</span><span class="hljs-keyword">if</span> (php_sapi_name() === <span class="hljs-string">'cli-server'</span>) { <span class="hljs-comment">// PHP web server</span>} <span class="hljs-keyword">else</span> { <span class="hljs-comment">// Other web server</span>}</span></code>
PHP-FIG (PHP Framework Interop Group): The PHP-FIG is a group of PHP framework representatives who, according to the PHP-FIG website, "talk about the commonalities between our projects and find ways we can work together."
PHP-FIG は、さまざまな PHP フレームワーク開発者で構成されるオープンな組織であり、彼らが提案する推奨事項は標準や要件ではなく、ベスト プラクティスの提案を集めたものに近いものです。ただし、Laravel や Symfony など、現在人気のある PHP フレームワークのほとんどはこれらの推奨事項に準拠しているため、多くの PHP ツールや多種多様なオープンソース ライブラリを使用したい場合は、これが現代の PHP の事実上の標準のように感じられます。この標準を採用するのが最善です。
The PHP-FIG's mission is framework interoperability. And framework interoperability means working together via interfaces, autoloading, and style.
以下で説明するように、PHP-FIG の使命は、異なるフレームワーク間の相互運用性であり、異なるフレームワークを簡単に組み合わせて一緒に使用できるようにします。相互運用性は現在、主にインターフェイス、自動読み込み、スタイル:
Interfaces enable PHP developers to build, share, and use specialized components instead of monolithic frameworks
の 3 つの側面を通じて実現されています。インターフェイスに基づいて、たとえば Laravel の HTTP のコンポーネントを直接使用できます。処理部分は、Symfony 全体を Laravel に統合することなく、Symfony Frameworks の symfony/httpfoundation コンポーネントを直接使用します。 PHP frameworks work together via autoloading. Autoloading is the process by which a PHP class is automatically located and loaded on-demand by the PHP interpreter during runtime
、オートローディング標準が登場する前は、PHP コンポーネントとフレームワークは __autoload()或spl_autoload_register()
メソッドに基づいて独自の独自のオートローダーを実装していました。そのため、サードパーティのコンポーネントを使用したい場合は、まず、オートローダーの実装を調査する必要があります。 PHP frameworks work together via code style.
PSR は PHP standards recommendation
の略で、PSR- などの PHP-FIG によって提案される推奨文書です。 1、PSR-2など各 PHP-FIG 推奨事項は、ほとんどの PHP フレームワークの開発で一般的に遭遇する問題を解決するために提案されています。
現在、PHP-FIG の公式 Web サイト http://www.php-fig.org/psr/ で、現在採用されているすべての推奨事項を確認できます。
特定の PSR 文書の内容については、公式 Web サイト PSR-1/2/3/4 を参照してください。
文档 | 原文 | 中文翻译 |
---|---|---|
PSR-1 Basic Coding Standard | https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-1-basic-coding-standard.md | https://segmentfault.com/a/1190000002521577 |
PSR-2 Coding Style Guide | https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md | https://segmentfault.com/a/1190000002521620 |
PSR-3 Logger Interface | https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md | https://segmentfault.com/a/1190000002521644 |
PSR-4 AutoLoading Standard | https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-4-autoloader.md | https://segmentfault.com/a/1190000002521658 |
PHP tags : 使用
Implement PSR-1 : 要求必须采用PSR-1
Indentation: 采用4个空格字符作为缩进
Files and lines : 必须使用Unix linefeed(LF)作为结尾;文件最后必须以空行作为结束;不能使用尾部 ?> PHP tag;每行尽量不要超过80个字符,最多不能超过120个字符;每行结尾不能包含空格;
Keywords: 所有的PHP关键字都必须小写
Namespaces: 每个namespace声明后面都必须跟着一个空行;使用use来import or alias namespaces的时候,必须在use声明后面跟一个空行;
Classes: 定义类时,开始的大括号(opening bracket)必须新起一行,结束的大括号(closing bracket)必须在类体定义后面新起一行;extents/implements关键字必须跟在类名定义的后面;例如:
<code class="language-php?linenums hljs xml"><span class="php"><span class="hljs-preprocessor"><?php</span><span class="hljs-keyword">namespace</span> <span class="hljs-title">My</span>\<span class="hljs-title">App</span>;<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Administrator</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">User</span> {</span> <span class="hljs-comment">// Class definition body</span>}</span></code>
Methods: 方法定义的大括号规则与类定义类似,opening brackets和closing brackets都必须新起一行。
Visibility: 对类中定义的全部property和method都必须声明可见性(visibility),可见性是public, protected, private其中的一个;abstract / final 必须写在visibility之前;static必须写在visibility之后;
Control structures : 所有的control structure keyword (if/elseif/else/switch/case/while/do while/for/foreach/try/catch)的后面都必须一个空字符;大括号的规则与class定义不同,opening brackets跟control structure keyword必须在同一行,而closing bracket必须另新一行;
我们可以通过IDE的格式化工具来让代码格式化,实现PSR-1和PSR-2的code style。我经常使用的工具PHPStorm就可以设置。还有一些其他工具,比如 PHP-CS-Fixer 或 PHP Code Sniffer
PSR-3 is an interface, and it prescribes methods that can be implemented by PHP logger components.
An autoloader is a strategy for finding a PHP class, interface, or trait and loading it into the PHP interpreter on-demand at runtime. PHP components and frameworks that support the PSR-4 autoloader standard can be located by and loaded into the PHP interpreter with only one autoloader.
关于PSR-4,看官方文档之后感觉理解很困惑,本书的作者的解释就非常简洁: The essence of PSR-4 is mapping a top-level namespaces prefix to a specific filesystem directory.
,简单来说,就是设定了一个namespaces前缀和某个特定的文件目录之间的映射关系,然后在这个namespace前缀之下如果还有更多的sub namespace,这些sub namespaces就会跟这个特定的目录下面的子目录一一映射起来。例如,\Oreilly\ModernPHP namespace与 src/ 物理路径一一映射,那么\Oreilly\ModernPHP\Chapter1对应的文件夹就是src/Chapter1,而\Oreilly\ModernPHP\Chapter1\Example类对应的文件路径就是src/Chapter1/Example.php文件。
PSR-4 lets you map a namespace prefix to a filesystem directory. The namespace prefix can be one top-level namespace. The namespace prefix can also be a top-level namespace and any number of subnamespaces. It's quite flexible.
Modern PHP is less about monolithic framework and more about composing solutions from specialized and interoperable components.
What Are Components?: A component is a bundle of code that helps solve a specific problem in your PHP application.
框架与Components:如果我们正在创建一个小项目,可以直接使用一些PHP Components集合来解决问题;如果我们正在进行一个多人合作开发的大项目,我们可以通过使用一个Framework;但这都不是绝对的,应该根据具体问题来解决。
Packagist:跟其他语言的包管理机制一样,例如Maven,也有一个网站 https://packagist.org/ 让我们搜索我们需要的PHP Components的相关信息。总所周知的原因,Packagist在国内很不稳定,可以使用国内的全量镜像来代替,http://www.phpcomposer.com/ 。
Composer is a dependency manager for PHP components taht runs on the command line
は、他の現代言語と同様に、PHP は、iOS の Cocoapods、Android の Maven/gradle、フロントエンドの npm、および Ruby gem と同様に、依存関係管理に Composer を使用します。これらのツールを使用すると、サードパーティ ライブラリの管理コストを大幅に簡素化できます。そのため、必要なコンポーネントを Packagist で見つけたら、Composer を通じてこのライブラリを使用できます。
Composer を使用してサードパーティ コンポーネントを追加すると、Composer は必要な PHP コンポーネントのダウンロードを自動的に支援するだけでなく、PSR-4 に準拠するオートローダーの作成も自動的に支援します。
Cocoapods と同様に、Cocoapods は Podfile を使用して、依存する必要があるサードパーティ ライブラリを指定し、現在使用している特定のサードパーティ ライブラリのバージョン番号を保存します。したがって、さまざまなメンバー/CI/開発環境やその他の場所で使用されるサードパーティ ライブラリのバージョンの一貫性を確保するために、これら 2 つのファイルをバージョン管理に追加して管理する必要があります。 Composer の対応するファイルは、composer.json とcomposer.lock です。ここで注意する必要があるのは、composer install コマンドと Composer update コマンドの違いです。
* Composer install は、composer.lock にリストされているものよりも上位のバージョンのコンポーネントをインストールしません。
* Composer update は、更新します。コンポーネントが最新の安定バージョンに更新されると、composer.lock ファイルも最新の PHP コンポーネントのバージョン番号に更新されます。
最新の PHP コンポーネントはセマンティック バージョニング スキームを使用し、小数点 (.) で区切られた 3 つの数字 (1.13.2 など) を含みます。同時に、これは他の多くの言語のオープンソース ライブラリのバージョン ルールでもあり、私は常にこれに興味を持っていましたが、ついに Modern PHP で対応する説明を目にしました。
この部分は、iOS の独自の仕様を作成するのと非常に似ています。それほど複雑な問題ではありません。参考書籍や公式ドキュメントを簡単に公開できます。 >