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——但这个头可以伪造,只有在"你自己的代理确实写了它"的前提下才可信。
常见坑清单
empty("0")是true,用它校验必填项会误杀。foreach用引用后忘记unset()。- 用
==比较用户输入的字符串,产生意外的类型转换。 - 数字字符串的键名会被转成 int(放数组篇细讲)。
isset()对null返回false,array_key_exists()才管 null 的情形。- 变量未定义时直接用会报
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。