PHP 基本语法 01|变量与数据类型:以 `$` 开头,但没有类型

变量就是一个带 $ 的盒子

PHP 的变量以 $ 开头,不需要声明,赋值即创建:

<?php
$name = "张三";
$age  = 28;
$price = 99.5;
$tags = ["php", "web"];
$isOk = true;

变量名区分大小写($Name 和 $name 是两个变量),但函数名和类名不区分。这条不一致的地方,很容易在改代码时踩到。

unset($x) 销毁变量;isset($x) 判断"已定义且不为 null";empty($x) 判断"不存在 / 为假值"。三者的区别是个高频考点:

<?php
$a = 0;
$b = "";
$c = null;

var_dump(isset($a));   // bool(true)   0 不算空
var_dump(empty($a));   // bool(true)   0 算"空"
var_dump(isset($c));   // bool(false)  null 视为未定义
var_dump(empty($c));   // bool(true)

empty() 的判断口径是"假值",不是"空"。字符串 "0"、整数 0、空数组 []、false、null 全被判为 empty。用 empty() 校验用户输入是有风险的——用户填个 0 会被当成没填。

八种基本类型

PHP 的类型不是声明出来的,而是运行时根据值决定的:

类型 示例 说明
bool true / false 布尔
int 42、0x1A、0b101、0o17 整数(PHP 7+ 支持下划线1_000_000)
float 3.14、1.2e3 浮点(也叫 double)
string 'abc'、"abc" 字符串
array [1, 2]、['k' => 'v'] 数组(PHP 里是哈希表,兼做索引/关联)
object new Foo() 对象
callable 'strlen'、fn() => 1 可调用结构(不是真实"类型",是类型声明用的)
null null 空值

另外还有一些复合/伪类型:iterable(数组或 Traversable)、mixed(8.0 起,任意值)、void(无返回)、never(8.1 起,永不返回)、resource(文件句柄之类的资源句柄)。

查看类型和值:

<?php
$v = 3.14;
var_dump($v);          // float(3.14)
echo gettype($v);      // double
settype($v, "integer"); // 就地转换
var_dump($v);          // int(3)

排查问题,直接 var_dump() 比 echo 可靠得多——echo 遇到数组或对象只会输出 Array。

类型转换:从"自动"到"手动"

PHP 的自动类型转换(juggle)非常宽容,这是它的便利,也是它最大的坑源。

<?php
$a = "10";    // string
$b = 5;       // int
var_dump($a + $b);   // int(15)  —— 数字字符串被转成数字
var_dump($a . $b);   // string(7) "105" —— 拼接总是字符串

显式转换用括号运算符或函数:

<?php
$int = (int)"123abc";      // 123,取开头数字,不报错
$int = (int)"abc";         // 0
$bool = (bool)"0";         // false
$bool = (bool)"0.0";       // true  —— 注意!只有 "0" 和 "" 是 false
$arr = (array)null;        // []
$str = (string)99.9;       // "99.9"

各函数作用基本一样:intval()、floatval()、strval()、boolval()。函数的好处是可以作为表达式嵌套使用。

那套"松散比较"规则,以及 PHP 8 的修正

== 会先做类型转换再比较,=== 要求类型和值都相同。经典对比:

<?php
var_dump(0 == "0");        // true
var_dump(0 == "");         // false  —— PHP 8 改了!
var_dump("abc" == 0);      // false  —— PHP 8 改了!PHP 7 里是 true
var_dump("1" == "01");     // true   字符串都能转为数字
var_dump("10" == "1e1");   // true
var_dump(100 == "1e2");    // true
var_dump(null == false);   // true
var_dump([] == false);     // true   空数组是假值
var_dump("abc" == "ABC");  // false  —— 字符串比较是区分大小写的

"abc" == 0 在 PHP 7 里是 true(因为 "abc" 转数字得 0),这是历史上无数安全漏洞的来源(比如绕过哈希校验)。PHP 8.0 修改了数字与字符串比较的规则:只有当字符串真的是数字字符串时才按数值比较,否则按字符串比较。所以 "abc" == 0 现在是 false。

即便如此,规则依然是:涉及判断就用 ===。in_array() 和 array_search() 也请带上第三个参数 true:

<?php
$list = [1, 2, 3];
var_dump(in_array("1", $list));        // true  —— 松散
var_dump(in_array("1", $list, true));  // false —— 严格

可变变量与引用

可变变量的语法是 $$,用得少但要知道:

<?php
$key = "name";
$$key = "李四";     // 等价于 $name = "李四"

引用(&)让两个变量指向同一份数据,这是 PHP 里最容易产生"莫名其妙被改掉"的东西:

<?php
$a = [1, 2, 3];
$b = &$a;          // 引用
$b[] = 4;
echo count($a);    // 4  —— $a 也被改了

常用的引用场景只有三个:函数里的 function foo(&$x)、foreach ($arr as &$v)、以及返回引用。其他场景建议少用。尤其 foreach 加引用之后必须 unset($v):

<?php
$nums = [1, 2, 3];
foreach ($nums as &$n) { $n *= 2; }
foreach ($nums as $n) { }   // ← 这里的 $n 还是引用,会覆盖最后一个元素
unset($n);                  // 必须解除引用
print_r($nums);             // [2, 4, 6]

常量

<?php
const MAX_RETRY = 3;              // 编译期常量,只能在顶层/类里
define("APP_NAME", "Demo");       // 运行期定义,可以在任何地方
echo PHP_EOL;                     // 预定义常量,跨平台换行
echo __DIR__, PHP_EOL, __FILE__, PHP_EOL, __LINE__;

区别:const 在编译期解析、可以出现在类和命名空间里,不能被重复定义;define() 可以动态定义(比如用变量拼名字)。项目配置推荐 const 或类常量,define() 留给"必须动态生成"的场合。

PHP 8.0 之后 define() 的默认行为变了(第三个参数 case_insensitive 被移除),常量名永远区分大小写。

几个必须知道的"魔术变量"

  • $GLOBALS:所有全局变量的数组
  • $_GET / $_POST / $_REQUEST / $_COOKIE / $_SESSION:请求数据
  • $_SERVER:服务器与请求信息(REQUEST_METHOD、REQUEST_URI、REMOTE_ADDR)
  • $_FILES:上传文件
  • $_ENV:环境变量(注意 variables_order 配置)

$_SERVER['REMOTE_ADDR'] 取的是连接的对端地址。如果前面有 Nginx 反向代理,它会是代理的 IP,真实 IP 要读 X-Forwarded-For——但这个头可以伪造,只有在"你自己的代理确实写了它"的前提下才可信。

常见坑清单

  1. empty("0") 是 true,用它校验必填项会误杀。
  2. foreach 用引用后忘记 unset()。
  3. 用 == 比较用户输入的字符串,产生意外的类型转换。
  4. 数字字符串的键名会被转成 int(放数组篇细讲)。
  5. isset() 对 null 返回 false,array_key_exists() 才管 null 的情形。
  6. 变量未定义时直接用会报 Warning(PHP 8 起是 Warning,不是致命错误),可用 ?? 兜底:
<?php
$count = $_GET['count'] ?? 0;                 // null 合并
$name  = $user['name'] ?? $user['default'] ?? "匿名";

?? 比 isset() 三元短得多,而且只对 null 生效(不是"假值"),这个语义差异很关键:0 ?? 5 得到 0,empty(0) ? 5 : 0 得到 5。