常量是一个名字,它代表的值在脚本运行期间永远不会改变。用 const MAX_USERS = 100; 或 define('MAX_USERS', 100); 声明它,然后直接使用名字,不加 $。
<?php
const SITE_NAME = "Coffee Corner";
define("MAX_ITEMS", 5);
echo SITE_NAME, "\n";
echo "You can order up to " . MAX_ITEMS . " items\n";
$price = 3.5;
const TAX_RATE = 0.2;
echo "Total: ", $price * (1 + TAX_RATE), "\n";
按照惯例,常量名使用 UPPER_SNAKE_CASE(全大写加下划线)。常量是全局的:在文件顶部声明的常量,在每个函数里都能看到,不需要 global。
define() 与 const
两者都能创建常量,但生效的时机不同。const 在 PHP 编译文件时解析,define() 则像其他函数调用一样在运行时执行。
| const | define() |
|---|
用在 if、循环、函数内部 | 不行 | 可以 |
| 名字由变量拼出 | 不行 | 可以 |
| 类常量 | 可以 | 不行 |
| 值可以是表达式 | 可以,由常量和字面量组成 | 可以,任意值 |
| 自动加上命名空间 | 是 | 否(要写完整名字) |
<?php
// const accepts expressions made of literals and other constants
const SECONDS_PER_DAY = 24 * 60 * 60;
const GREETING = "Hello, " . "world";
// define() can run conditionally and take a computed name
$env = "dev";
if ($env === "dev") {
define("DEBUG", true);
}
foreach (["RED" => "#f00", "GREEN" => "#0f0"] as $name => $hex) {
define("COLOR_" . $name, $hex);
}
echo SECONDS_PER_DAY, "\n";
echo GREETING, "\n";
var_dump(DEBUG);
echo COLOR_GREEN, "\n";
默认使用 const,因为它更短,并且在编译时就会检查。当常量取决于某个条件或需要计算出名字时,再使用 define()。
常量不能修改
给常量赋值是一个解析错误:
<?php
const LIMIT = 10;
LIMIT = 20;
PHP Parse error: syntax error, unexpected token "=" in /home/index.php on line 3
把同一个常量定义两次不会报错,但 PHP 会输出一条警告,并保留第一次的值:
<?php
define("LIMIT", 10);
define("LIMIT", 20);
echo LIMIT; // 10
Warning: Constant LIMIT already defined in /home/index.php on line 3
如果某个值可能已经设置过(例如一个会被加载多次的配置文件),先用 defined() 检查:
<?php
if (!defined("APP_ENV")) {
define("APP_ENV", "production");
}
if (!defined("APP_ENV")) {
define("APP_ENV", "testing"); // skipped
}
echo APP_ENV, "\n";
var_dump(defined("APP_ENV"));
var_dump(defined("NOT_THERE"));
// Read a constant whose name is in a string
$name = "APP_ENV";
echo constant($name), "\n";
常量数组
常量可以保存数组。你可以用 NAME[key] 读取它、遍历它、把它传来传去,但不能修改它。
<?php
const WEEKDAYS = ["Mon", "Tue", "Wed", "Thu", "Fri"];
define("HTTP_CODES", [200 => "OK", 404 => "Not Found", 500 => "Server Error"]);
echo WEEKDAYS[0], "\n";
echo count(WEEKDAYS), " weekdays\n";
echo HTTP_CODES[404], "\n";
foreach (HTTP_CODES as $code => $text) {
echo "$code $text\n";
}
类常量
在类里面,const 声明的是属于这个类的常量。在类外部用 ClassName::NAME 读取,在类内部用 self::NAME 或 static::NAME。类常量可以设置访问控制,可以是 final 的,从 PHP 8.3 开始还可以声明类型。
<?php
class Order
{
public const STATUS_NEW = "new";
public const STATUS_PAID = "paid";
private const MAX_LINES = 50;
final public const string CURRENCY = "EUR"; // typed, PHP 8.3
public function __construct(public string $status = self::STATUS_NEW) {}
public function canAddLine(int $lines): bool
{
return $lines < self::MAX_LINES;
}
}
$order = new Order();
echo $order->status, "\n";
echo Order::STATUS_PAID, "\n";
echo Order::CURRENCY, "\n";
var_dump($order->canAddLine(10));
// Dynamic access by name
$which = "STATUS_PAID";
echo constant("Order::$which"), "\n";
像 STATUS_NEW 和 STATUS_PAID 这样一组相关的类常量,通常更适合写成枚举,这样还能防止不合法的值被传来传去。
魔术常量:LINE、FILE、DIR 等
魔术常量的值取决于它出现的位置,在编译时填入。
<?php
namespace App\Billing;
class Invoice
{
public function describe(): string
{
return "class: " . __CLASS__ . "\n"
. "method: " . __METHOD__ . "\n"
. "function: " . __FUNCTION__ . "\n";
}
}
echo (new Invoice())->describe();
echo "namespace: ", __NAMESPACE__, "\n";
echo "line: ", __LINE__, "\n";
echo "file: ", basename(__FILE__), "\n";
echo "dir is a directory: ", var_export(is_dir(__DIR__), true), "\n";
最常用的是 __DIR__,也就是当前文件所在的目录。用它拼接引入路径,无论脚本是从哪个目录启动的,路径都能正常工作:require __DIR__ . '/config.php';。完整写法见 include 和 require。
值得了解的内置常量
PHP 自己定义了很多常量。下面这些经常会用到:
<?php
echo "PHP_VERSION: ", PHP_VERSION, "\n";
echo "PHP_INT_MAX: ", PHP_INT_MAX, "\n";
echo "PHP_INT_SIZE: ", PHP_INT_SIZE, " bytes\n";
echo "PHP_FLOAT_EPSILON: ", PHP_FLOAT_EPSILON, "\n";
echo "M_PI: ", M_PI, "\n";
echo "PHP_OS_FAMILY: ", PHP_OS_FAMILY, "\n";
echo "E_ALL: ", E_ALL, "\n";
echo "PHP_EOL is a newline: ", var_export(PHP_EOL === "\n", true), PHP_EOL;
PHP_EOL 是 PHP 所在系统的换行符(在 Linux 和 macOS 上是 "\n",在 Windows 上是 "\r\n")。它用于写入文件和终端的文本;在 HTML 输出中换行符不会显示出来,所以那里要用 <br>。