原文連接php
A short, practical guide for common and confusing PHP taskshtml
一個簡短的實用指南,用於常見的和使人困惑的PHP任務mysql
This document was last reviewed on January 25, 2019. It was last changed on February 20, 2019. It’s maintained by me, Alex Cabal. I’ve been writing PHP for a long time now, and currently I run Scribophile, an online writing group for serious writers, Writerfolio, an easy online writing portfolio for freelancers, and Standard Ebooks, an open source project that produces liberated ebooks for the true book lover. Drop me a line if you think I can help you with something, or with suggestions or corrections to this document.git
這份文件最後一次審查是在2019年1月25日。最後一次更改是在2019年2月20日。 它是由我Alex Cabal維護的。我已經編寫PHP很長時間了,目前我運行於Scribophile,這是一個面向嚴肅做家的在線寫做小組,Writerfolio,這是一個面向自由做家的簡單的在線寫做組合,還有Standard Ebooks,這是一個開源項目,爲真正的圖書愛好者提供免費的電子書。 若是你認爲我能幫你作些什麼,或者對這份文件有什麼建議或修改,請給我寫信。程序員
PHP is a complex language that has suffered years of twists, bends, stretches, and hacks. It’s highly inconsistent and sometimes buggy. Each version has its own unique features, warts, and quirks, and it’s hard to keep track of what version has what problems. It’s easy to see why it gets as much hate as it does sometimes.github
PHP是一種複雜的語言,經歷了多年的歪曲、誇大和詆譭。這是高度不合邏輯和反常的。每一個版本都有本身獨特的特性、缺點和怪癖,很難跟蹤哪一個版本有哪些問題。很容易理解爲何有時候會有那麼多的仇恨。web
Despite that, it’s the most popular language on the web today. Because of its long history, you’ll find lots of tutorials on how to do basic things like password hashing and database access. The problem is that out of five tutorials, you have a good chance of finding five totally different ways of doing something. Which way is the 「right」 way? Do any of the other ways have subtle bugs or gotchas? It’s really hard to find out, and you’ll be bouncing around the internet trying to pin down the right answer.正則表達式
儘管如此,它仍然是當今網絡上最流行的語言。因爲其悠久的歷史,您將發現許多教程,如何作基本的事情,如密碼哈希和數據庫訪問。問題是,在五篇教程中,您頗有可能找到五種徹底不一樣的方法。哪條路是「正確」的?其餘方法是否有細微的bug或陷阱?這真的很難找到,你會在互聯網上跳來跳去試圖找到正確的答案。算法
That’s also one of the reasons why new PHP programmers are so frequently blamed for ugly, outdated, or insecure code. They can’t help it if the first Google result was a four-year-old article teaching a five-year-old method!sql
這也是爲何新的PHP程序員常常由於醜陋、過期或不安全的代碼而受到指責的緣由之一。若是谷歌的第一個結果是四年前的一篇文章教的是五年前的方法,那他們就沒辦法了!
This document tries to address that. It’s an attempt to compile a set of basic instructions for what can be considered best practices for common and confusing issues and tasks in PHP. If a low-level task has multiple and confusing approaches in PHP, it belongs here.
本文檔試圖解決這個問題。它試圖編寫一套基本的操做指南,用於PHP中常見和使人困惑的問題和任務的最佳實踐。若是一個低階任務在PHP中有多種使人困惑的方法,那麼它就屬於這裏。
It’s a guide suggesting the best direction to take when facing one of the common low-level tasks a PHP programmer might encounter that are unclear because of the many options PHP might offer. For example: connecting to a database is a common task with a large amount of possible solutions in PHP, not all of them good ones—thus, it’s included in this document.
這是一份指南,建議在面對PHP程序員可能遇到的常見低階任務時採起的最佳方向。因爲PHP可能提供許多選項,這些任務並不清楚。例如:鏈接到數據庫是一項常見的任務,在PHP中有大量可能的解決方案,但並非全部的都是好的解決方案——所以,本文包含了這些解決方案。
It’s a series of short, introductory solutions. Examples should get you up and running in a basic setting, and you should do your own research to flesh them out into something useful to you.
它是一系列簡短的介紹性解決方案。示例應該讓您在基本設置中啓動和運行,而且您應該本身進行研究,將它們充實成對您有用的內容。
It points to what we consider the state-of-the-art of PHP. However, this means that if you’re using an older version of PHP, some of the features required to pull off these solutions might not be available to you. This is a living document that I’ll do my best to keep updated as PHP continues to evolve.
它指出了什麼是咱們認爲的最早進的PHP。然而,這意味着若是您使用的是較老版本的PHP,那麼實現這些解決方案所需的一些特性可能對您來講是不可用的。 這是一個動態的文檔,隨着PHP的不斷髮展,我將盡力保持更新。
This document is not a PHP tutorial. You should learn the basics and syntax of the language elsewhere.
本文檔不是PHP教程。您應該在其餘地方學習該語言的基礎知識和語法。
It’s not a guide to common web application problems like cookie storage, caching, coding style, documentation, and so on.
它不是關於常見web應用程序問題(如cookie存儲、緩存、編碼風格、文檔等)的指南。
It’s not a security guide. While it touches upon some security-related issues, you’re expected to do your own research when it comes to securing your PHP apps. In particular, you should carefully review any solution proposed here before implementing it. Your code, and your copy and paste, is your own fault.
它不是安全指南。雖然它涉及到一些與安全相關的問題,可是當涉及到保護PHP應用程序時,您須要本身進行研究。特別是,在實現它以前,您應該仔細檢查這裏提出的任何解決方案。您的代碼,以及您的複製和粘貼,都是您本身的錯誤。
It’s not an advocate of a certain coding style, pattern, or framework.
它並不提倡某種編碼風格、模式或框架
It’s not an advocate for a certain way of doing high-level tasks like user registration, login systems, etc. This document is strictly for low-level tasks that, because of PHP’s long history, might be confusing or unclear.
它並不提倡用某種方式來完成高級任務,好比用戶註冊、登陸系統等等。本文檔嚴格適用於低層任務,因爲PHP的歷史很長,這些任務可能使人困惑或不清楚。
It’s not a be-all and end-all solution, nor is it the only solution. Some of the methods described below might not be what’s best for your particular situation, and there are lots of different ways of achieving the same ends. In particular, high-load web apps might benefit from more esoteric solutions to some of these problems.
它不是萬能的解決方案,也不是惟一的解決方案。下面描述的一些方法可能並不適合您的特定狀況,而且有許多不一樣的方法能夠達到相同的目的。特別是,高負載web應用程序可能會受益於這些問題的一些更深奧的解決方案
PHP 7.2.10-0ubuntu0.18.04.1, installed on Ubuntu 18.04 LTS.
PHP is the 100-year-old tortoise of the web world. Its shell is inscribed with a rich, convoluted, and gnarled history. In a shared-hosting environment, its configuration might restrict what you can do.
PHP是web世界的百年老古董。它的外殼上鐫刻着豐富、曲折、多節的歷史。在共享宿主環境中,其配置可能會限制您的操做
In order to retain a scrap of sanity, we’re going to focus on just one version of PHP: PHP 7.2.10-0ubuntu0.18.04.1. This is the version of PHP you’ll get if you install it using apt-get on an Ubuntu 18.04 LTS server. In other words, it’s the sane default used by many.
爲了保持一點理智,咱們只關注PHP的一個版本:PHP 7.2.10-0ubuntu0.18.04.1。若是您在Ubuntu 18.04 LTS服務器上使用apt-get安裝PHP,就會獲得這個版本。換句話說,這是許多人使用的正常默認值。
You might find that some of these solutions work on different or older versions of PHP. If that’s the case, it’s up to you to research the implications of subtle bugs or security issues in these older versions.
您可能會發現,其中一些解決方案適用於不一樣或較老版本的PHP。若是是這樣的話,就由您來研究這些舊版本中細微的bug或安全問題的含義。
Use the built-in password hashing functions to hash and compare passwords.
使用內置的密碼散列函數散列和比較密碼。
Hashing is the standard way of protecting a user’s password before it’s stored in a database. Many common hashing algorithms like md5 and even sha1 are unsafe for storing passwords, because hackers can easily crack passwords hashed using those algorithms.
哈希是在用戶密碼存儲到數據庫以前保護其密碼的標準方法。許多常見的哈希算法,如md5甚至sha1,對於存儲密碼都是不安全的,由於黑客能夠很容易地破解使用這些算法哈希的密碼。
<?php
// Hash the password. $hashedPassword will be a 60-character string.
$hashedPassword = password_hash('my super cool password', PASSWORD_DEFAULT);
// You can now safely store the contents of $hashedPassword in your database!
// Check if a user has provided the correct password by comparing what they typed with our hash
password_verify('the wrong password', $hashedPassword); // false
password_verify('my super cool password', $hashedPassword); // true
?>
複製代碼
Many sources will recommend that you also 「salt」 your password before hashing it. That’s a great idea, and password_hash() already salts your password for you. That means that you don’t have to salt it yourself.
許多資料來源會建議您在散列密碼以前也要「加鹽」。這是個好主意,可是password_hash()已經爲您作了「加鹽」。這意味着你不須要本身再加鹽。
Use PDO and its prepared statement functionality.
使用PDO及其準備好的語句功能。
There are many ways to connect to a MySQL database in PHP. PDO (PHP Data Objects) is the newest and most robust of them. PDO has a consistent interface across many different types of database, uses an object-oriented approach, and supports more features offered by newer databases.
在PHP中,有許多方法能夠鏈接到MySQL數據庫。PDO (PHP數據對象)是其中最新的、最健壯的。PDO具備跨許多不一樣類型數據庫的一致接口,使用面向對象的方法,並支持更新數據庫提供的更多特性。
You should use PDO’s prepared statement functions to help prevent SQL injection attacks. Using the bindValue() function ensures that your SQL is safe from first-order SQL injection attacks. (This isn’t 100% foolproof though, see Further Reading for more details.) In the past, this had to be achieved with some arcane combination of 「magic quote」 functions. PDO makes all that gunk unnecessary.
您應該使用PDO的預處理語句函數來幫助防止SQL注入攻擊。使用bindValue()函數能夠確保SQL不會受到一階SQL注入攻擊。(不過,這並非100%萬無一失的,更多細節請參閱後續閱讀。)在過去,這必須經過一些神祕的「magic quote」函數組合來實現。PDO讓全部的奇技淫巧都沒有必要了。
<?php
// Create a new connection.
// You'll probably want to replace hostname with localhost in the first parameter. // Note how we declare the charset to be utf8mb4. This alerts the connection that we'll be passing UTF-8 data. This may not be required depending on your configuration, but it'll save you headaches down the road if you're trying to store Unicode strings in your database. See "Gotchas".
// The PDO options we pass do the following:
// PDO::ATTR_ERRMODE enables exceptions for errors. This is optional but can be handy.
// PDO::ATTR_PERSISTENT disables persistent connections, which can cause concurrency issues in certain cases. See "Gotchas".
$link = new PDO( 'mysql:host=your-hostname;dbname=your-db;charset=utf8mb4',
'your-username',
'your-password',
array(
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_PERSISTENT => false
)
);
$handle = $link->prepare('select Username from Users where UserId = ? or Username = ? limit ?');
$handle->bindValue(1, 100);
$handle->bindValue(2, 'Bilbo Baggins');
$handle->bindValue(3, 5);
$handle->execute();
// Using the fetchAll() method might be too resource-heavy if you're selecting a truly massive amount of rows. // If that's the case, you can use the fetch() method and loop through each result row one by one.
// You can also return arrays and other things instead of objects. See the PDO documentation for details.
$result = $handle->fetchAll(PDO::FETCH_OBJ);
foreach($result as $row){
print($row->Username);
}
?>
複製代碼
Not having set the character set to utf8mb4 in the connection string might cause Unicode data to be stored incorrectly in your database, depending on your configuration.
在鏈接字符串中沒有將字符集設置爲utf8mb4可能會致使Unicode數據在數據庫中存儲不正確,這取決於您的配置。
Even if you declare your character set to be utf8mb4, make sure that your actual database tables are in the utf8mb4 character set. For why we use utf8mb4 instead of just utf8, check the PHP and UTF-8 section.
即便將字符集聲明爲utf8mb4,也要確保實際的數據庫表是utf8mb4字符集。 要了解爲何咱們使用utf8mb4而不是utf8,請檢查PHP和UTF-8部分。
Enabling persistent connections can possibly lead to weird concurrency-related issues. This isn’t a PHP problem, it’s an app-level problem. Persistent connections are safe to use as long as you consider the consequences. See this Stack Overflow question.
啓用持久鏈接可能會致使奇怪的併發相關問題。這不是PHP問題,而是應用程序級別的問題。只要考慮到後果,使用持久鏈接是安全的。參見這個堆棧溢出問題
Use <?php ?>.
There are a few different ways to delimit blocks of PHP: <?php ?>, <?= ?>, <? ?>, and <% %>. While the shorter ones might be more convenient to type, they’re disabled by default and must be enabled by configuring the PHP server with the short_open_tag option. Therefore the only method that’s guaranteed to work on all PHP servers is <?php ?>. If you ever plan on deploying your PHP to a server whose configuration you can’t control, then you should always use <?php ?>.
有幾種不一樣的方法來分隔PHP塊:<?php ?>, <?= ?>, <? ?>, 和 <% %>。雖然較短的選項可能更便於輸入,但它們在默認狀況下是禁用的,必須使用short_open_tag選項配置PHP服務器來啓用它們。所以,保證在全部PHP服務器上工做的惟一方法是<?php ? >。若是您計劃將PHP部署到沒法控制其配置的服務器,那麼應該始終使用<?php ? >。
Fortunately <?= is available regardless of whether or not short tags are enabled, so it’s always safe to use that shorthand instead of <? php print() ?>.
幸運的是<?= 是可用的,無論是否啓用了短標記,因此使用這種簡寫代替<?php print () ? >也是安全的。
If you’re only coding for yourself and have control over the PHP configuration you’ll be using, you might find the shorter tags to be more convenient. But remember that <? ?>; might conflict with XML declarations and <% %> is actually ASP style. Whatever you choose, make sure you stay consistent!
若是您只是爲本身編寫代碼,而且可以控制將要使用的PHP配置,那麼您可能會發現更短的標記更方便。可是記得<? ?>可能與XML聲明衝突,<% %>其實是ASP風格。不管你選擇什麼,必定要保持一致!
Gotchas 陷阱
When including a closing ?> tag in a pure PHP file (for example, in a file that only contains a class definition), make sure not to leave any trailing newlines after it. While the PHP parser safely 「eats」 a single newline character after the closing tag, any other newlines might be outputted to the browser and possibly confuse things if you’re outputting any HTTP headers later.
當在純PHP文件中包含一個關閉 ?>標記時(例如,在一個只包含類定義的文件中),請確保不要在它後面留下任何尾隨的換行符。雖然PHP解析器在結束標記以後安全地「吃掉」了一個換行符,可是任何其餘換行符均可能輸出到瀏覽器中,若是稍後輸出任何HTTP頭信息,可能會形成混淆。
When writing a web app targeting older versions of IE, make sure not to leave a newline between any closing ?>; tag and the html <!doctype> tag. Old versions of IE will enter quirks mode if they encounter any white space, including newlines, before the doctype declaration. This isn’t an issue for newer versions of IE and other, more advanced browsers. (Read: every other browser besides IE.)
當編寫一個針對較老版本IE的web應用程序時,請確保在任何結束符?>標籤和html <!doctype之間不要留下新的換行。由於舊版本的IE在doctype聲明以前遇到任何空白(包括換行),就會進入quirks模式。對於更新版本的IE和其餘更高級的瀏覽器來講,這不是問題。(閱讀:除IE以外的全部其餘瀏覽器。)
PHP provides several ways to auto-load files containing classes that haven’t yet been loaded. The older way is to use a magic global function called __autoload(). However you can only have one __autoload() function defined at once, so if you’re including a library that also uses the __autoload() function, then you’ll have a conflict.
PHP提供了幾種方法來自動加載包含還沒有加載的類的文件。較老的方法是使用一個名爲__autoload()的全局魔術函數。可是,您一次只能定義一個__autoload()函數,因此若是您包含一個也使用了__autoload()函數的庫,那麼您將會遇到衝突。
The correct way to handle this is to name your autoload function something unique, then register it with the spl_autoload_register() function. This function allows more than one __autoload() function to be defined, so you won’t step on any other code’s own __autoload() function.
處理此問題的正確方法是將autoload函數命名爲唯一的,而後使用spl_autoload_register()函數註冊它。這個函數容許定義多個_autoload()函數,所以您不會踩到任何其餘代碼本身的_autoload()函數。
<?php
// First, define your auto-load function.
function MyAutoload($className){
include_once($className . '.php');
}
// Next, register it with PHP.
spl_autoload_register('MyAutoload');
// Try it out!
// Since we haven't included a file defining the MyClass object, our auto-loader will kick in and include MyClass.php. // For this example, assume the MyClass class is defined in the MyClass.php file. $var = new MyClass(); ?> 複製代碼
A lot of ink has been spilled about whether to define strings with single quotes (‘) or double quotes (「). Single-quoted strings aren’t parsed, so whatever you’ve put in the string, that’s what will show up. Double-quoted strings are parsed and any PHP variables in the string are evaluated. Additionally, escaped characters like \n for newline and \t for tab are not evaluated in single-quoted strings, but are evaluated in double-quoted strings.
關因而使用單引號(')定義字符串仍是使用雙引號(")定義字符串,已經花費了大量的筆墨。單引號字符串不會被解析,因此不管你在字符串中放入了什麼,都會顯示出來。雙引號字符串解析並計算字符串中的任何PHP變量。此外,換行的\n和製表符的\t等轉義字符不在單引號字符串中求值,而是在雙引號字符串中求值。
Because double-quoted strings are evaluated at run time, the theory is that using single-quoted strings will improve performance because PHP won’t have to evaluate every single string. While this might be true on a certain scale, for the average real-life application the difference is so small that it doesn’t really matter. So for an average app, it doesn’t matter what you choose. For extremely high-load apps, it might matter a little. Make a choice depending on what your app needs, but whatever you choose, be consistent.
由於雙引號字符串是在運行時計算的,因此理論上使用單引號字符串將提升性能,由於PHP沒必要計算每一個字符串。雖然這在必定程度上多是正確的,可是對於實際的應用程序來講,這種差別很是小,因此實際上並不重要。因此對於一個普通的應用,你選擇什麼並不重要。對於很是高負載的應用程序,這可能有點關係。根據您的應用程序的須要作出選擇,可是不管您選擇什麼,都要保持一致。
A caching system can often improve your app’s performance. Memcached is a popular choice and it works with many languages, including PHP.
緩存系統一般能夠提升應用程序的性能。Memcached是一個流行的選擇,它能夠與許多語言兼容,包括PHP。
However, when it comes to accessing a Memcached server from a PHP script, you have two different and very stupidly named choices of client library: Memcache and Memcached. They’re different libraries with almost the same name, and both are used to access a Memcached instance.
然而,當涉及到從PHP腳本訪問Memcached服務器時,您有兩個不一樣的客戶端擴展庫選擇,它們的名稱都很是愚蠢:Memcache和Memcached。它們是名稱幾乎相同的不一樣庫,都用於訪問Memcached實例。
It turns out that the Memcached library is the one that best implements the Memcached protocol. It includes a few useful features that the Memcache library doesn’t, and seems to be the one most actively developed.
事實證實,Memcached庫是最好地實現Memcached協議的庫。它包含一些Memcache庫沒有的有用特性,並且彷佛是發展最活躍的。
However if you don’t need to access a Memcached instance from a series of distributed servers, then use APCu instead. APCu is supported by the PHP project and has much of the same functionality as Memcached.
可是,若是不須要從一系列分佈式服務器訪問Memcached實例,則使用APCu。APCu由PHP項目支持,而且具備與Memcached相同的功能。
Installing the Memached client library After you install the Memcached server, you need to install the Memcached client library. Without the library, your PHP scripts won’t be able to communicate with the Memcached server.
安裝Memached客戶端擴展庫 安裝Memcached服務器以後,須要安裝Memcached客戶機庫。若是沒有這個庫,您的PHP腳本將沒法與Memcached服務器通訊。
You can install the Memcached client library on Ubuntu 16.04 by running this command in your terminal:
你能夠在你的終端上運行這個命令在Ubuntu 16.04上安裝Memcached客戶端庫:
user@localhost: sudo apt-get install php-memcached
複製代碼
Before Ubuntu 14.04, the APC project was both an opcode cache and a Memcached-like key-value store. Since the version of PHP that ships since Ubuntu 14.04 now includes a built-in opcode cache, APC was split into the APCu project, which is essentially APC’s key-value storage functionality—AKA the 「user cache」, or the 「u」 in APCu—without the opcode-cache parts.
在Ubuntu 14.04以前,APC項目既是一個操做碼緩存,也是一個相似memcache的鍵值存儲。因爲Ubuntu 14.04以後發佈的PHP版本如今包含了一個內置的操做碼緩存,APC被分割成APCu項目,APCu項目本質上是APC的key-value存儲功能——也就是APCu中的「user cache」或「u」——沒有操做碼緩存部分。
You can install APCu on Ubuntu 16.04 by running this command in your terminal:
你能夠經過在你的終端運行這個命令在Ubuntu 16.04上安裝APCu:
sudo apt-get install php-apcu
複製代碼
<?php
// Store some values in the APCu cache. We can optionally pass a time-to-live, but in this example the values will live forever until they're garbage-collected by APCu. apcu_store('username-1532', 'Frodo Baggins'); apcu_store('username-958', 'Aragorn'); apcu_store('username-6389', 'Gandalf'); // You can store arrays and objects too. apcu_store('creatures', array('ent', 'dwarf', 'elf')); apcu_store('saruman', new Wizard()); // After storing these values, any PHP script can access them, no matter when it's run!
$value = apcu_fetch('username-958', $success);
if($success === true){
print($value); // Aragorn
}
$value = apcu_fetch('creatures', $success);
if($success === true){
print_r($value);
}
$value = apcu_fetch('username-1', $success); // $success will be set to boolean false, because this key doesn't exist. if($success !== true){ // Note the !==, this checks for true boolean false, not "falsey" values like 0 or empty string. print('Key not found'); } apcu_delete('username-958'); // This key will no longer be available. ?> 複製代碼
If you’re migrating APCu code from a version of APUc before 16.04, note that the function names have changed from apc_* to apcu_*. For example, apc_store() became apcu_store().
若是您正在從16.04以前的APUc版本遷移APCu代碼,請注意函數名已經從apc_更改成apcu_。例如,apc_store()變成了apcu_store()。
Traditionally in PHP you would define constants using the define() function. But at some point PHP gained the ability to also declare constants with the const keyword. Which one should you use when defining your constants?
在PHP中,一般使用define()函數定義常量。但在某個時候,PHP也得到了使用const關鍵字聲明常量的能力。定義常量時應該使用哪個?
The answer lies in the little differences between the two methods.
答案在於這兩種方法之間的細微差異。
define() defines constants at run time, while const defines constants at compile time. This gives const a very slight speed edge, but not one worth worrying about unless you’re building large-scale software.
define()在運行時定義常量,而const在編譯時定義常量。這給了const一個很是小的速度優點,可是不值得擔憂,除非您正在構建大型軟件。
define() puts constants in the global scope, although you can include namespaces in your constant name. That means you can’t use define() to define class constants.
define()將常量放在全局範圍內,不過能夠在常量名稱中包含名稱空間。這意味着您不能使用define()來定義類常量。
define() lets you use expressions both in the constant name and in the constant value, unlike const which allows neither. This makes define() much more flexible.
define()容許您在常量名稱和常量值中同時使用表達式,而const不容許這二者。這使得define()更加靈活。
define() can be called within an if() block, while const cannot.
define()能夠在if()塊中調用,而const不能。
<?php
// Let's see how the two methods treat namespaces namespace MiddleEarthCreatures\Dwarves; const GIMLI_ID = 1; define('MiddleEarth\Creatures\Elves\LEGOLAS_ID', 2); print(\MiddleEarth\Creatures\Dwarves\GIMLI_ID); // 1 print(\MiddleEarth\Creatures\Elves\LEGOLAS_ID); // 2; note that we used define(), but the namespace is still recognized // Now let's declare some bit-shifted constants representing ways to enter Mordor.
define('TRANSPORT_METHOD_SNEAKING', 1 << 0); // OK!
const TRANSPORT_METHOD_WALKING = 1 << 1; // Compile error! const can't use expressions as values // Next, conditional constants. define('HOBBITS_FRODO_ID', 1); if($isGoingToMordor){ define('TRANSPORT_METHOD', TRANSPORT_METHOD_SNEAKING); // OK! const PARTY_LEADER_ID = HOBBITS_FRODO_ID // Compile error: const can't be used in an if block
}
// Finally, class constants
class OneRing{
const MELTING_POINT_CELSIUS = 1000000; // OK!
define('MELTING_POINT_ELVISH_DEGREES', 200); // Compile error: can't use define() within a class } ?> 複製代碼
Because define() is ultimately more flexible, it’s the one you should use to avoid headaches unless you specifically require class constants. Using const generally results in more readable code, but at the expense of flexibility. Whichever one you use, be consistent!
由於define()最終更加靈活,因此應該使用它來避免麻煩,除非特別須要類常量。使用const一般會產生更易於閱讀的代碼,但這是以靈活性爲代價的。 不管使用哪個,都要保持一致!
In older versions of PHP, every time a script was executed it would have to be compiled from scratch, even if it had been compiled before. Opcode caches were additional software that saved previously compiled versions of PHP, speeding things up a bit. There were various flavors of caches you could choose from.
在較老版本的PHP中,每次執行腳本時都必須從頭編譯,即便以前已經編譯過了。操做碼緩存是另一種軟件,它能夠保存之前編譯過的PHP版本,從而稍微加快速度。有各類各樣的緩存可供選擇。
Lucky for us, the version of PHP that ships with Ubuntu 18.04 includes a built-in opcode cache that’s turned on by default. So there’s nothing for you to do!
幸運的是,附帶Ubuntu 18.04的PHP版本包含一個默認打開的內置操做碼緩存。因此你什麼不用作了!
Before PHP 7 came around, PHP had two different ways of using regular expressions: the PCRE (Perl-compatible, preg_) functions and the POSIX (POSIX extended, ereg_) functions.
在PHP 7出現以前,PHP有兩種使用正則表達式的方法:PCRE (perl兼容的preg_)函數和POSIX (POSIX extended, ereg_)函數。
Each family of functions used a slightly different flavor of regular expression. Luckily for us, the ereg_* functions have been removed in PHP 7, so this source of confusion is past us.
每一個函數家族使用的正則表達式略有不一樣。幸運的是,在PHP 7中已經刪除了ereg_*函數,因此這個困惑的根源已通過去了。
Remember to use the /u flag when working with regexes, to ensure you’re working in Unicode mode.
記住,在使用regexes時使用/u標誌,以確保在Unicode模式下工做。
There are several ways of configuring a web server to serve PHP. Back in the stone age, we would use Apache’s mod_php. Mod_php attaches PHP to Apache itself, but Apache does a very bad job of managing it. You’ll suffer from severe memory problems as soon as you get any kind of real traffic.
有幾種配置web服務器來服務PHP的方法。回到石器時代,咱們使用Apache的mod_php。Mod_php將PHP附加到Apache自己,可是Apache在管理PHP方面作得很是糟糕。一旦你有了真正的流量,你就會有嚴重的內存問題。
Two new options soon became popular: mod_fastcgi and mod_fcgid. Both of these keep a limited number of PHP processes running, and Apache sends requests to these interfaces to handle PHP execution on its behalf. Because these libraries limit how many PHP processes are alive, memory usage is greatly reduced without affecting performance.
兩個新選項很快流行起來:mod_fastcgi和mod_fcgid。這兩種方法都保持有限數量的PHP進程運行,Apache向這些接口發送請求,以表明它處理PHP執行。因爲這些庫限制了有多少PHP進程是活動的,所以內存使用大大減小,而不會影響性能。
Some smart people created an implementation of fastcgi that was specially designed to work really well with PHP, and they called it PHP-FPM. This was the standard solution for web servers since Ubuntu 12.04.
一些聰明的人建立了一個fastcgi的實現,這個實現是專門爲PHP而設計的,他們稱之爲PHP- fpm。這是自Ubuntu 12.04以來web服務器的標準解決方案。
In the years since Ubuntu 12.04, Apache introduced a new method of interacting with PHP-FPM: mod_proxy_fcgi. We’ll use this module to route PHP requests received by Apache to the FPM instance.
自Ubuntu 12.04以來,Apache引入了一種與PHP-FPM交互的新方法:mod_proxy_fcgi。咱們將使用這個模塊將Apache接收到的PHP請求路由到FPM實例。
The following example is for Apache 2.4.29, but PHP-FPM also works for other web servers like Nginx.
下面的示例適用於Apache 2.4.29,可是PHP-FPM也適用於其餘web服務器,好比Nginx。
You can install PHP-FPM and Apache on Ubuntu 18.04 by running these command in your terminal:
sudo apt-get install apache2 php-fpm
sudo a2enmod proxy_fcgi rewrite
複製代碼
First, we’ll create a new PHP FPM pool that will serve our app. Paste the following into /etc/php/7.2/fpm/pool.d/mysite.conf:
[mysite]
user = www-data
group = www-data
listen = /run/php/mysite.sock
listen.owner = www-data
listen.group = www-data
pm = ondemand
pm.max_children = 10
複製代碼
(Note that you can include many other very interesting options when configuring PHP-FPM pools. Of particular interest is the php_admin_value[include_path] option.)
(注意,在配置PHP-FPM池時,您能夠包含許多其餘很是有趣的選項。特別有趣的是php_admin_value[include_path]選項。)
Next, we’ll configure our Apache virtualhost to route PHP requests to the PHP-FPM process. Place the following in your Apache configuration file (in Ubuntu the default one is /etc/apache2/sites-available/000-default.conf; if you're using the default configuration, paste this into the existing < VirtualHost > directive).
接下來,咱們將配置Apache虛擬主機,將PHP請求路由到PHP- fpm進程。將如下內容放入Apache配置文件中(在Ubuntu中,默認配置文件是/etc/apache2/site -available/000-default.conf;若是使用默認配置,請將其粘貼到現有配置中< VirtualHost >指令)。
<VirtualHost *:80>
<Directory />
Require all granted
</Directory>
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} \.php$
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_FILENAME} -f
RewriteRule . proxy:unix:/run/php/mysite.sock|fcgi://localhost%{DOCUMENT_ROOT}%{REQUEST_FILENAME} [P]
</VirtualHost>
複製代碼
Finally, restart Apache and the FPM process:
最後重啓Apache和FPM進程:
sudo systemctl restart apache2.service php7.2-fpm.service
複製代碼
Tested with PHPMailer 6.0.6.
PHP provides a mail() function that looks enticingly simple and easy. Unfortunately, like a lot of things in PHP, its simplicity is deceptive and using it at face value can lead to serious security problems.
PHP提供了一個看起來很是簡單的mail()函數。不幸的是,就像PHP中的許多東西同樣,它的簡單性具備欺騙性,表面上使用它會致使嚴重的安全問題。
Email is a set of protocols with an even more tortured history than PHP. Suffice it to say that there are so many gotchas in sending email that just being in the same room as PHP’s mail() function should give you the shivers.
電子郵件是一組協議,其歷史甚至比PHP還要痛苦。我只想說,在發送電子郵件時有太多的陷阱,僅僅與PHP的mail()函數在同一個房間就會讓您毛骨悚然。
PHPMailer is a popular and well-aged open-source library that provides an easy interface for sending mail securely. It takes care of the gotchas for you so you can concentrate on more important things.
PHPMailer是一個流行的、歷史悠久的開放源碼庫,它爲安全地發送郵件提供了一個簡單的接口。它會幫你解決問題,這樣你就能把精力集中在更重要的事情上。
<?php
// Include the PHPMailer library
require_once('phpmailer-5.2.7/PHPMailerAutoload.php');
// Passing 'true' enables exceptions. This is optional and defaults to false.
$mailer = new PHPMailer(true);
// Send a mail from Bilbo Baggins to Gandalf the Grey
// Set up to, from, and the message body. The body doesn't have to be HTML; check the PHPMailer documentation for details. $mailer->Sender = 'bbaggins@example.com'; $mailer->AddReplyTo('bbaggins@example.com', 'Bilbo Baggins'); $mailer->SetFrom('bbaggins@example.com', 'Bilbo Baggins'); $mailer->AddAddress('gandalf@example.com'); $mailer->Subject = 'The finest weed in the South Farthing'; $mailer->MsgHTML('<p>You really must try it, Gandalf!</p><p>-Bilbo</p>'); // Set up our connection information. $mailer->IsSMTP(); $mailer->SMTPAuth = true; $mailer->SMTPSecure = 'ssl'; $mailer->Port = 465; $mailer->Host = 'my smtp host'; $mailer->Username = 'my smtp username'; $mailer->Password = 'my smtp password'; // All done! $mailer->Send(); ?> 複製代碼
A common task your web app might need to do is to check if a user has entered a valid email address. You’ll no doubt find online a dizzying range of complex regular expressions that all claim to solve this problem, but the easiest way is to use PHP’s built-in filter_var() function, which can validate email addresses.
web應用程序可能須要作的一項常見任務是檢查用戶是否輸入了有效的電子郵件地址。毫無疑問,您將在網上發現一系列使人眼花繚亂的複雜正則表達式,它們都聲稱能夠解決這個問題,可是最簡單的方法是使用PHP內置的filter_var()函數,該函數能夠驗證電子郵件地址。
<?php
filter_var('sgamgee@example.com', FILTER_VALIDATE_EMAIL); // Returns "sgamgee@example.com". This is a valid email address.
filter_var('sauron@mordor', FILTER_VALIDATE_EMAIL); // Returns boolean false! This is *not* a valid email address.
?>
複製代碼
Tested with HTML Purifier 4.10.0.
使用 HTML淨化器4.10.0 測試。
When displaying user input in any web application, it’s essential to 「sanitize」 it first to remove any potentially dangerous HTML. A malicious user can craft HTML that, if outputted directly by your web app, can be dangerous to the person viewing it.
在任何web應用程序中顯示用戶輸入時,必須首先對其進行「清理」,以刪除任何潛在的危險HTML。惡意用戶能夠建立HTML,若是您的web應用程序直接輸出HTML,可能會對查看它的人形成危險。
While it may be tempting to use regular expressions to sanitize HTML, do not do this. HTML is a complex language and it’s virtually guaranteed that any attempt you make at using regular expressions to sanitize HTML will fail.
雖然使用正則表達式來清理HTML可能很誘人,可是不要這樣作。HTML是一種複雜的語言,幾乎能夠確定,使用正則表達式對HTML進行清理的任未嘗試都將失敗。
You might also find advice suggesting you use the strip_tags() function. While strip_tags() is technically safe to use, it’s a 「dumb」 function in the sense that if the input is invalid HTML (say, is missing an ending tag), then strip_tags() might remove much more content than you expected. As such it’s not a great choice either, because non-technical users often use the < and > characters in communications.
您可能還會發現建議使用strip_tags()函數的建議。雖然strip_tags()在技術上是安全的,但它是一個「愚蠢」的函數,由於若是輸入是無效的HTML(好比缺乏結束標記),那麼strip_tags()可能會刪除比您預期多得多的內容。所以,這也不是一個很好的選擇,由於非技術用戶常常在通訊中使用<和>字符。
If you read the section on validating email addresses, you might also be considering using the filter_var() function. However the filter_var() function has problems with line breaks, and requires non-intuitive configuration to closely mirror the htmlentities() function. As such it’s not a good choice either.
若是您閱讀了關於驗證電子郵件地址的部分,您可能還在考慮使用filter_var()函數。可是filter_var()函數存在換行問題,而且須要非直觀的配置來緊密地反映htmlentities()函數。所以,這也不是一個好的選擇。
If your web app only needs to completely escape (and thus render harmless, but not remove entirely) HTML, use PHP’s built-in htmlentities() function. This function is much faster than HTML Purifier, because it doesn’t perform any validation on the HTML—it just escapes everything.
若是您的web應用程序只須要徹底轉義HTML(從而呈現無害的HTML,但不須要徹底刪除),那麼使用PHP的內置htmlentities()函數。這個函數比HTML淨化器快得多,由於它不執行HTML上的任何驗證—它只是轉義全部內容。
htmlentities() differs from its cousin htmlspecialchars() in that it encodes all applicable HTML entities, not just a small subset.
htmlentities()與其近親htmlspecialchars()的不一樣之處在於,它編碼全部適用的HTML實體,而不只僅是一個小子集。
<?php
// Oh no! The user has submitted malicious HTML, and we have to display it in our web app!
$evilHtml = '<div onclick="xss();">Mua-ha-ha! Twiddling my evil mustache...</div>';
// Use the ENT_QUOTES flag to make sure both single and double quotes are escaped.
// Use the UTF-8 character encoding if you've stored the text as UTF-8 (as you should have). // See the UTF-8 section in this document for more details. $safeHtml = htmlentities($evilHtml, ENT_QUOTES, 'UTF-8'); // $safeHtml is now fully escaped HTML. You can output $safeHtml to your users without fear! ?> 複製代碼
For many web apps, simply escaping HTML isn’t enough. You probably want to entirely remove any HTML, or allow a small subset of HTML through. To do this, use the HTML Purifier library.
對於許多web應用程序來講,僅僅轉義HTML是不夠的。您可能但願徹底刪除任何HTML,或者容許HTML的一小部分經過。爲此,使用HTML淨化器庫。
HTML Purifier is a well-tested but slow library. That’s why you should use htmlentities() if your requirements aren’t that complex, because it will be much, much faster.
HTML淨化器是一個通過良好測試但速度很慢的庫。這就是爲何若是您的需求沒有那麼複雜,那麼應該使用htmlentities(),由於它會快得多。
HTML Purifier has the advantage over strip_tags() because it validates the HTML before sanitizing it. That means if the user has inputted invalid HTML, HTML Purifier has a better chance of preserving the intended meaning of the HTML than strip_tags() does. It’s also highly customizable, allowing you to whitelist a subset of HTML to keep in the output.
HTML淨化器比strip_tags()更有優點,由於它在對HTML進行清理以前驗證它。這意味着若是用戶輸入了無效的HTML, HTML淨化器比strip_tags()更有可能保留HTML的預期含義。它也是高度可定製的,容許您在輸出中保留HTML的一個子集。
The downside is that it’s quite slow, it requires some setup that might not be feasible in a shared hosting environment, and the documentation is often complex and unclear. The following example is a basic configuration; check the documentation to read about the more advanced features HTML Purifier offers.
缺點是它很是慢,在一些共享主機環境中可能還須要作一些安裝配,並且文檔一般很複雜且不清楚。下面的例子是一個基本配置;查看文檔以瞭解HTML淨化器提供的更高級特性。
<?php
// Include the HTML Purifier library
require_once('htmlpurifier-4.6.0/HTMLPurifier.auto.php');
// Oh no! The user has submitted malicious HTML, and we have to display it in our web app!
$evilHtml = '<div onclick="xss();">Mua-ha-ha! Twiddling my evil mustache...</div>';
// Set up the HTML Purifier object with the default configuration.
$purifier = new HTMLPurifier(HTMLPurifier_Config::createDefault());
$safeHtml = $purifier->purify($evilHtml); // $safeHtml is now sanitized. You can output $safeHtml to your users without fear!
?>
複製代碼
Using htmlentities() with the wrong character encoding can result in surprising output. Always make sure that you specify a character encoding when calling the function, and that it matches the encoding of the string being sanitized. See the UTF-8 section for more details.
使用帶有錯誤字符編碼的htmlentities()可能會致使驚人的輸出。始終確保在調用函數時指定了一個字符編碼,而且該字符編碼與要清除的字符串的編碼匹配。有關更多細節,請參見UTF-8部分。
Always include the ENT_QUOTES and character encoding parameters when using htmlentities(). By default, htmlentities() doesn’t encode single quotes. What a dumb default!
在使用htmlentities()時,始終包含ENT_QUOTES和字符編碼參數。默認狀況下,htmlentities()不編碼單引號。多麼愚蠢的默認!
HTML Purifier is extremely slow for complex HTML. Consider setting up a caching solution like APCu to store the sanitized result for later use.
對於複雜的HTML, HTML淨化器很是慢。考慮設置一個相似APCu的緩存解決方案來存儲通過清理的結果,以供之後使用。
UTF-8 in PHP sucks. Sorry. PHP中的UTF-8糟透了。對不起。
Right now PHP does not support Unicode at a low level. There are ways to ensure that UTF-8 strings are processed OK, but it’s not easy, and it requires digging in to almost all levels of the web app, from HTML to SQL to PHP. We’ll aim for a brief, practical summary.
目前,PHP在較低的級別上不支持Unicode。有一些方法能夠確保處理好UTF-8字符串,但這並不容易,並且須要深刻到web應用程序的幾乎全部級別,從HTML到SQL再到PHP。咱們的目標是一個簡短的,實用的總結。
The basic string operations, like concatenating two strings and assigning strings to variables, don’t need anything special for UTF-8. However most string functions, like strpos() and strlen(), do need special consideration. These functions often have an mb_* counterpart: for example, mb_strpos() and mb_strlen(). Together, these counterpart functions are called the Multibyte String Functions. The multibyte string functions are specifically designed to operate on Unicode strings.
基本的字符串操做,好比鏈接兩個字符串並將字符串賦值給變量,對於UTF-8不須要任何特殊的操做。然而,大多數字符串函數,如strpos()和strlen(),確實須要特別考慮。這些函數一般有一個mb_*對應的函數:例如,mb_strpos()和mb_strlen()。這些對應的函數統稱爲多字節字符串函數。多字節字符串函數是專門針對Unicode字符串設計的。
These functions aren’t installed by default in Ubuntu 18.04. You can install them with:
這些函數在Ubuntu 18.04中默認不安裝。你能夠用如下軟件安裝:
sudo apt install php-mbstring
複製代碼
You must use the mb_* functions whenever you operate on a Unicode string. For example, if you use substr() on a UTF-8 string, there’s a good chance the result will include some garbled half-characters. The correct function to use would be the multibyte counterpart, mb_substr().
不管什麼時候對Unicode字符串進行操做,都必須使用mb_*函數。例如,若是在UTF-8字符串上使用substr(),頗有可能結果中包含一些混淆的半字符。要使用的正確函數是多字節對應的mb_substr()。
The hard part is remembering to use the mb_* functions at all times. If you forget even just once, your Unicode string has a chance of being garbled during further processing.
最難的部分是始終記住使用mb_*函數。若是您只忘記一次,您的Unicode字符串有可能在進一步處理期間被打亂。
Not all string functions have an mb_* counterpart. If there isn’t one for what you want to do, then you might be out of luck.
並非全部字符串函數都有一個mb_*對應項。若是沒有一個適合你想作的事情,那麼你可能就不走運了。
Additionally, you should use the mb_internal_encoding() function at the top of every PHP script you write (or at the top of your global include script), and the mb_http_output() function right after it if your script is outputting to a browser. Explicitly defining the encoding of your strings in every script will save you a lot of headaches down the road.
此外,您應該在編寫的每一個PHP腳本的頂部(或全局包含腳本的頂部)使用mb_internal_encoding()函數,若是腳本要輸出到瀏覽器,則在它以後使用mb_http_output()函數。在每一個腳本中顯式地定義字符串編碼將爲您省去不少麻煩。
Finally, many PHP functions that operate on strings have an optional parameter letting you specify the character encoding. You should always explicitly indicate UTF-8 when given the option. For example, htmlentities() has an option for character encoding, and you should always specify UTF-8 if dealing with such strings.
最後,許多操做字符串的PHP函數都有一個可選參數,容許您指定字符編碼。當提供該選項時,應該始終顯式地指出UTF-8。例如,htmlentities()有一個字符編碼選項,若是處理這樣的字符串,應該始終指定UTF-8。
Often you’ll find yourself writing files with contents or filenames encoded in some flavor of Unicode. PHP is able to run on a variety of operating systems, including Linux and Windows; but sadly how it handles Unicode filenames differs on each platform due to OS-level quirks.
一般,您會發現本身編寫的文件的內容或文件名以某種Unicode格式編碼。PHP可以運行在各類操做系統上,包括Linux和Windows;但遺憾的是,因爲os級別的怪癖,它處理Unicode文件名的方式在每一個平臺上都有所不一樣。
Linux and OSX appear to handle UTF-8 filenames fairly well. Windows, however, doesn’t. If you try to use PHP to write to a file with non-ASCII characters in the filename in Windows, you may discover that the filename is displayed with strange or corrupted characters.
Linux和OSX彷佛能夠很好地處理UTF-8文件名。然而,Windows沒有。若是您嘗試使用PHP在Windows中寫入文件名中含有非ascii字符的文件,您可能會發現文件名顯示的字符很奇怪或已損壞。
There doesn’t seem to be an easy, portable workaround here. In Linux and OSX you can encode your filenames with UTF-8, but in Windows you have to remember to encode using ISO-8859-1.
這裏彷佛沒有一種簡單、便攜的工做方式。在Linux和OSX中,您可使用UTF-8對文件名進行編碼,可是在Windows中,您必須記住使用ISO-8859-1進行編碼。
If you don’t want to bother with having your script check if it’s running on Windows or not, you could always URL encode all of your filenames before writing them. This effectively works around Unicode quirks by representing Unicode characters by a subset of ASCII.
若是您不想麻煩檢查您的腳本是否在Windows上運行,您能夠在編寫它們以前對全部的文件名進行URL編碼。經過用ASCII的子集表示Unicode字符,這有效地解決了Unicode的一些怪癖。
If your PHP script accesses MySQL, there’s a chance your strings could be stored as non-UTF-8 strings in the database even if you follow all of the precautions above.
若是您的PHP腳本訪問MySQL,那麼即便您遵循了上面的全部預防措施,您的字符串也有可能以非utf -8字符串的形式存儲在數據庫中。
To make sure your strings go from PHP to MySQL as UTF-8, make sure your database and tables are all set to the utf8mb4 character set and collation, and that you use the utf8mb4 character set in the PDO connection string. For an example, see the section on connecting to and querying a MySQL database. This is critically important.
要確保字符串從PHP轉換到MySQL爲UTF-8,請確保數據庫和表都設置爲utf8mb4字符集和排序規則,並在PDO鏈接字符串中使用utf8mb4字符集。例如,請參閱關於鏈接和查詢MySQL數據庫的部分。 這一點相當重要。
Note that you must use the utf8mb4 character set for complete UTF-8 support, not the utf8 character set! See Further Reading for why.
注意,必須使用utf8mb4字符集來支持完整的UTF-8,而不是utf8字符集!緣由請參閱進一步閱讀。
Use the mb_http_output() function to ensure that your PHP script outputs UTF-8 strings to your browser. In your HTML, include the charset meta tag in your page’s tag.
使用mb_http_output()函數確保PHP腳本將UTF-8字符串輸出到瀏覽器。在HTML中,在頁面的標記中包含charset元標記。
<?php
// Tell PHP that we're using UTF-8 strings until the end of the script mb_internal_encoding('UTF-8'); // Tell PHP that we'll be outputting UTF-8 to the browser
mb_http_output('UTF-8');
// Our UTF-8 test string
$string = 'Êl síla erin lû e-govaned vîn.';
// Transform the string in some way with a multibyte function
// Note how we cut the string at a non-Ascii character for demonstration purposes
$string = mb_substr($string, 0, 15);
// Connect to a database to store the transformed string
// See the PDO example in this document for more information
// Note that we define the character set as utf8mb4 in the PDO connection string
$link = new PDO( 'mysql:host=your-hostname;dbname=your-db;charset=utf8mb4',
'your-username',
'your-password',
array(
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_PERSISTENT => false
)
);
// Store our transformed string as UTF-8 in our database
// Your DB and tables are in the utf8mb4 character set and collation, right?
$handle = $link->prepare('insert into ElvishSentences (Id, Body) values (?, ?)');
$handle->bindValue(1, 1);
$handle->bindValue(2, $string);
$handle->execute();
// Retrieve the string we just stored to prove it was stored correctly
$handle = $link->prepare('select * from ElvishSentences where Id = ?');
$handle->bindValue(1, 1);
$handle->execute();
// Store the result into an object that we'll output later in our HTML $result = $handle->fetchAll(PDO::FETCH_OBJ); ?><!doctype html> <html> <head> <meta charset="utf-8" /> <title>UTF-8 test page</title> </head> <body> <?php foreach($result as $row){ print($row->Body); // This should correctly output our transformed UTF-8 string to the browser } ?> </body> </html> 複製代碼
In the bad old days of PHP we had to work with dates and times using a bewildering combination of date(), gmdate(), date_timezone_set(), strtotime(), and so on. Sadly you’ll still find lots of tutorials online featuring these difficult and old-fashioned functions.
在PHP糟糕的舊時代,咱們必須使用日期()、gmdate()、date_timezone_set()、strtotime()等使人困惑的組合來處理日期和時間。遺憾的是,您仍然能夠在網上找到許多教程,其中介紹了這些困難而過期的功能。
Fortunately for us, the version of PHP we’re talking about features the much friendlier DateTime class. This class encapsulates all the functionality and more of the old date functions in one easy-to-use class, with the bonus of making time zone conversions much simpler. Always use the DateTime class for creating, comparing, changing, and displaying dates in PHP.
幸運的是,咱們正在討論的PHP版本具備友好得多的DateTime類。這個類將全部的功能和更多的舊日期函數封裝在一個易於使用的類中,而且使時區轉換更加簡單。始終使用DateTime類在PHP中建立、比較、更改和顯示日期。
<?php
// Construct a new UTC date. Always specify UTC unless you really know what you're doing! $date = new DateTime('2011-05-04 05:00:00', new DateTimeZone('UTC')); // Add ten days to our initial date $date->add(new DateInterval('P10D')); print($date->format('Y-m-d h:i:s')); // 2011-05-14 05:00:00 // Sadly we don't have a Middle Earth timezone
// Convert our UTC date to the PST (or PDT, depending) time zone
$date->setTimezone(new DateTimeZone('America/Los_Angeles'));
// Note that if you run this line yourself, it might differ by an hour depending on daylight savings
print($date->format('Y-m-d h:i:s')); // 2011-05-13 10:00:00
$later = new DateTime('2012-05-20', new DateTimeZone('UTC'));
// Compare two dates
if($date < $later){
print('Yup, you can compare dates using these easy operators!');
}
// Find the difference between two dates
$difference = $date->diff($later);
print('The 2nd date is ' . $difference->days . ' later than 1st date.');
?>
複製代碼
If you don’t specify a time zone, DateTime::__construct() will set the resulting date’s time zone to the time zone of the computer you’re running on. This can lead to spectacular headaches later on. Always specify the UTC time zone when creating new dates unless you really know what you’re doing.
若是沒有指定時區,DateTime:: _construct()將把結果日期的時區設置爲您正在運行的計算機的時區。這可能會在之後致使嚴重的頭痛。建立新日期時,必定要指定UTC時區,除非您真的知道本身在作什麼
If you use a Unix timestamp in DateTime::__construct(), the time zone will always be set to UTC regardless of what you specify in the second argument.
若是在DateTime:: _construct()中使用Unix時間戳,則不管在第二個參數中指定什麼,時區都將始終設置爲UTC
Passing zeroed dates (e.g. 「0000-00-00」, a value commonly produced by MySQL as the default value in a DateTime column) to DateTime::__construct() will result in a nonsensical date, not 「0000-00-00」.
將帶0的日期(例如「00:00 -00-00」,這是MySQL一般在DateTime列中生成的默認值)傳遞給DateTime::……construct()會獲得一個無心義的日期,而不是「00:00 -00-00」。
Using DateTime::getTimestamp() on 32-bit systems will not represent dates past 2038. 64-bit systems are OK.
在32位系統上使用DateTime::getTimestamp()不會表示超過2038年的日期。64位系統沒有問題。
PHP’s loose typing system offers many different ways of checking a variable’s value. However it also presents a lot of problems. Using == to check if a value is null or false can return false positives if the value is actually an empty string or 0. isset() checks whether a variable has a value that is not null, but doesn’t check against boolean false.
PHP的鬆散類型系統提供了許多不一樣的方法來檢查變量的值。然而,它也提出了許多問題。若是值其實是空字符串或0,則使用==檢查值是否爲null或false則會返回錯誤的信息。isset()檢查變量的值是否爲非null,但不檢查布爾值是否爲false。
The is_null() function accurately checks if a value is null, and the is_bool() function checks if it’s a boolean value (like false), but there’s an even better option: the === operator. === checks if the values are identical, which is not the same as equivalent in PHP’s loosely-typed world. It’s also slightly faster than is_null() and is_bool(), and looks nicer than using a function for comparison.
函數is_null()精確地檢查值是否爲空,is_bool()函數檢查值是否爲布爾值(相似於false),可是還有一個更好的選項:===操做符。===檢查值是否相同,這在PHP的鬆散類型世界中並不等同於等效值。它也比is_null()和is_bool()稍快一些,而且看起來比使用函數進行比較要好。
<?php
$x = 0;
$y = null;
// Is $x null?
if($x == null){
print('Oops! $x is 0, not null!');
}
// Is $y null?
if(is_null($y)){
print('Great, but could be faster.');
}
if($y === null){
print('Perfect!');
}
// Does the string abc contain the character a?
if(strpos('abc', 'a')){
// GOTCHA! strpos returns 0, indicating it wishes to return the position of the first character.
// But PHP interpretes 0 as false, so we never reach this print statement!
print('Found it!');
}
//Solution: use !== (the opposite of ===) to see if strpos() returns 0, or boolean false.
if(strpos('abc', 'a') !== false){
print('Found it for real this time!');
}
?>
複製代碼
When testing the return value of a function that can return either 0 or boolean false, like strpos(), always use === and !==, or you’ll run in to problems.
在測試能夠返回0或boolean false的函數的返回值時,如strpos(),始終使用===和!==,不然會遇到問題。
Most web guides will suggest using PHP’s iconv() function to remove diacritics. However iconv() often has trouble with UTF-8 input and will sometimes produce surprising errors.
大多數web指南都建議使用PHP的iconv()函數刪除變音符號。可是iconv()常常在UTF-8輸入方面有問題,有時會產生使人驚訝的錯誤。
A better way is to use PHP’s intl library. It can be installed with:
更好的方法是使用PHP的intl庫。可安裝:
sudo apt install php-intl
複製代碼
Once you have it installed, use the Transliterator class to remove diacritics from text:
安裝後,使用Transliterator類從文本中刪除變音符號:
<?php
$transliterator = Transliterator::createFromRules(':: Any-Latin; :: Latin-ASCII; :: NFD; :: [:Nonspacing Mark:] Remove; :: NFC;', Transliterator::FORWARD);
print($transliterator->transliterate('Êl síla erin lû e-govaned vîn.'));
?>
複製代碼
Thanks for reading! If you haven’t figured it out already, PHP is complex and filled with pitfalls. Since I’m only human, there might be mistakes in this document.
感謝你的閱讀!若是您尚未弄清楚,那麼PHP是複雜的,而且充滿了陷阱。因爲我只是個凡人,這份文件可能會有錯誤。
If you’d like to contribute to this document with suggestions or corrections, please contact me using the information in the last revised & maintainers section.
若是您想爲這個文檔提供建議或更正,請使用上一個修訂和維護部分中的信息與我聯繫。