重头学PHP-2:基本语法

301 阅读13分钟

PHP-2: 基本语法

1. 什么是 php 程序?

1.1 php 是什么

  • PHP: (PHP: Hypertext Preprocessor), 超文本预处理器的缩写
  • PHP 是开源的,免费的,运行在服务端的,用来动态生成网页数据的脚本语言
  • 所谓脚本语言,是指不需要编译,直接由解释器/虚拟机执行的编程语言

1.2 php 程序执行流程

  • php 程序是用 php 指令编写并由 php 解析器执行的代码
  • php 程序必须使用.php做为扩展名
  • php 程序可以使用标签方式嵌入到 html 文档中

php1

执行流程哪下:

  1. 客户端请求服务器端的 php 程序
  2. 服务器端将 php 程序转发给 php 解释器执行
  3. php 解释器执行完毕将生成的 html 或其它内容返回到服务器
  4. 服务器将最终生成的 html 代码做为响应内容返回客户端

1.3 php 集成运行环境

初学者推荐使用集成环境,省去手工逐一安装配置麻烦

序号 集成环境 操作系统 描述
1 phpStudy Windows / Linux 中文,免费,功能全, 推荐
2 MAMP MacOS 英文,收费, 功能全, 推荐

1.4 php 程序文档

序号 组成 描述
1 <?php ... ?> PHP 代码标记
2 ; 分号 语句分隔符,代码块使用右大括号
3 空白符 合理使用空白符可增强代码可读性
4 注释 // 单行注释, /* 多行注释 */

php 标记之外的内容会原样返回客户端,如 html 代码

示例代码: demo1.php

<?php
/*
1. 功能: 求和
2. 参数: 整数
3. 返回: 整数
*/
function sum(int $a, int $b): int
{
    // 返回结果
    return $a + $b;
}

// 函数调用
echo sum(10, 20);
?>

1.5 打印结果

序号 指令 描述
1 echo 语言结构, 可查看多个变量
2 print 语言结构,功能与echo类似,区别是有返回值
3 print_r() 函数,以更容易理解的格式打印变量信息,常用于数组
4 var_dump() 函数,可查看一个变量更多信息,如类型
5 var_export() 函数,输出或返回一个变量的字符串表示(源代码)

实际工作中, echovar_dump()基本可以满足大多数需求

示例代码: demo2.php

<?php
# php打印结果

$email = 'admin@php.cn';

echo $email, '<br>';
echo print $email;
echo '<br>';
var_dump($email);
echo '<br>';
var_export($email);

/* 运行结果
admin@php.cn
admin@php.cn1
string(12) "admin@php.cn"
'admin@php.cn'
*/

2. php 变量一网打尽

2.1 变量是什么

概念 描述
临时存储数据的空间 并不所有数据都需要保存到文件中,对于只用于当前程序的数据用变量更合适
数据复用的手段 将程序需要反复调用的数据,保存到一个变量中, 可以实现数据复用

2.2 命名规范

序号 规则 描述
1 语法 $ + 标识符 (必须以$做为变量标志)
2 标识符 [a-z][A-Z][0-9]_ 只允许使用大小写英文字母,数字或下划线, 且不能以数字开始
3 大小写敏感 严格区分大小写: $name$Name是完全不同的变量

示例代码: demo3.php

# 变量命名

$username = 'admin';
$userName = 'peter';

// 区分大小写
echo $username, '<br>', $userName, '<hr>';

// 无效变量名
// 没有使用$开头
// Hello = 'php';
// 标识符以数字开始
// $123aaa = 100;
// 标识符使用了特殊字符
// $user@id = 20;

// 有意义但不推荐, 如中文变量名
$我的邮箱 = 'peter@php.cn';
echo $我的邮箱;

// 无意义变量名
$aaa = 'php.cn';
$_ = 123;
$_123 = 888;
// 变量要做到望名生义, 一个有意义的变量名,可以极大提升代码的可读性和可维护性

推荐一个变量命名网站,告别命名恐惧症:unbug.github.io/codelf/

2.3 创建方式

php 是动态语言,所以它的类型,值和变量名都是动态的

序号 名称 描述
1 弱类型 变量的类型,由它的当前值决定
2 变量传递 变量的值来自另一个变量时,有"值传递与引用传递"二种方式
3 动态变量名 也叫"可变变量", 即变量标识符可以来自另一个变量的值

2.3.1 弱类型

  • php 是弱类型语言, 它的变量类型,由赋给它的决定
  • 因此, php 变量也不需要使用前进行声明

示例代码: demo4.php

# php变量是弱类型

$var = 100;
var_dump($var);
echo '<br>';
$var = 'Hello World';
var_dump($var);

/*
int(100)
string(11) "Hello World"
*/

2.3.2 值传递与引用传递

将变量赋值给另一个变量时,有值传递与引用传递二种方式

序号 传递方式 描述
1 值传递 将原变量的副本(变量值)复制到新变量中
2 引用传递 将原变量内存访问地址&引用赋值给新变量

变量的值保存在"栈"中, 引用地址保存在"堆"中, 想深入了解请学习数据结构知识

示例代码: demo5.php

# 值传递与引用传递

// 1. 值传递
$price1 = 99;
// 将price1的值,传递给变量price2
$price2 = $price1;

// 更新price2
$price2 = 199;

// 查看price1当前值:99
echo 'price1 = ' . $price1;

echo '<hr>';

// 2. 引用传递
$price1 = 99;
// 将price1的引用(可理解为地址),传递给变量price2
// 注意, 在变量price1前添加一个&:地址引用符
$price2 = &$price1;

// 更新price2
$price2 = 199;

// 查看price1当前值: 199
echo 'price1 = ' . $price1;

// 此时,price1已经随price2同步更新为199

2.3.3 可变变量

  • 可变变量: 是指变量的标识符可以动态的改变,即变量标识符可以来自另一个变量的值
  • 应用场景: 需要通过动态改变变量来处理不同需求的时候,例如图像处理,请求处理时

示例代码: demo6.php

?php
# 可变变量

$var = 'email';
?var = 'admin@php.cn';
// 等价于
// $email = 'admin@php.cn';

echo $email;

2.4 检测与删除

序号 函数 描述
1 isset() 检测变量是否存在且值非null
2 unset() 删除变量,无返回值
3 empty() 是否为空(""/0/'0'/null/false/[]/\$a)
4 is_null() NULL: 赋值为null/未赋值/unset()
error_reporting(E_ALL);
// $username 未定义, 直接输出会报钷
echo $username;
// isset($var): 如果变量$username存在且不为null,则输出它,此时不再报错
if (isset($username)) echo $username . '<br>';

$username = 'peter zhu';
echo $username, '<br>';
// 销毁/删除变量$username, 无返回值
unset($username);
// 报错:变量未定义
echo $username;
// 如果定义变量而没有初始化/赋值
$email;
// NULL
var_dump($email);
var_dump(is_null($email));
// empty(): 判断变量是否是空值,null只是它的子集之一,应用范围更广泛
$var = '';
var_dump(empty($var));  // true
var_dump(is_null($var)); // false
// 所以,检测表单值是否为空, 应该用empty()

2.5 数据类型

变量的访问方式,受以下条件的限制

序号 名称 描述
1 数据类型 主要有基本类型, 复用类型,特殊类型
2 作用域 变量的有效范围,即可见性,查询变量的工具
3 生命周期* 变量从创建到注销的全过程(程序结束会自动注销)
  • 确定了数据类型, 才可以确定数据的 "取值范围" 与 "操作方式",所以非常重要
  • 变量数据类型由值决定, 值的数据类型有三类:

2.5.1 基本类型

基本类型: 是构成复合类型的基本数据单元

序号 类型 标识符 检测函数 举例
1 整数 integer is_int() 150, 999
2 浮点数 float is_float() 3.14, .315
2 字符 string is_string() 'php', "email"
4 布尔 boolean is_bool() true, false

2.5.2 复合类型

序号 类型 标识符 检测函数 举例
1 对象 object is_object() new stdClass()
2 数组 array is_array() $arr = [1,2,3]

2.5.3 特殊类型

序号 类型 标识符 检测函数 举例
1 null is_object() new stdClass()
2 资源 resource is_resource() $f = fopen(...)

示例代码: demo7.php

<?php
# 变量类型

// 基本类型
$name = '手机';
$price = 3890;
$is5G = true;
echo $name . '价格: ' . $price . ', 是否5G? ' . ($is5G ? '是' : '否') . '<br>';

// 复合类型
$obj = new stdClass;
$obj->email = 'admin@php.cn';
echo $obj->email, '<br>';

$arr = ['电脑', 8000, 'huawei'];
// print_r($arr);
echo '<pre>' . print_r($arr, true) . '</pre>';

// 特殊类型
$num = null;
var_dump($num);
$f = fopen('demo6.php', 'r');
var_dump($f);
// gettype(): 返回类型字符串
echo gettype($f);

2.5.4 类型查询

  • 类型检测函数,如is_int()返回的都是一个布尔值
  • gettype(): 返回类型的字符串表示

2.6 类型转换

2.6.1 自动转换

  • 自动转换: 表达式根据操作符, 将操作数转为一致的数据类型后再进行运算
  • 自动转换, 通常只发生在 基本类型 参与的算术或字符串运算中
序号 类型 转换规则
1 null null => 0
2 boolean true => 1, false => 0
3 string 123abc => 123, abc123 => 0
4 integer int => float

2.6.2 强制转换

  • 强制转换分为: 临时转换永久转换 二种
  • 临时转换, 可使用类型提示符,或者类型函数实现

使用类型提示符:

序号 类型 转换规则
1 (int) 转为整数
2 (float) 转为浮点数
3 (string) 转为字符串
4 (array) 转为数组
5 (object) 转为对象

使用类型函数:

序号 类型 转换规则
1 intval() 转为整数
2 floatval() 转为浮点数
3 strval() 转为字符串
4 boolval() 转为布尔
  • 永久转换: 使用函数settype($var , $type)

示例代码: demo8.php

<?php
# 变量类型转换

// 自动转换

$a = null;
$b = true;
$c = false;
$d = '5g';
$e = 'php';
$f = 15;

echo $a + 10, '<br>';
echo $b + 10, '<br>';
// 字符串转数值型会有警告,但代码仍会执行, 推荐使用强制转换
echo $d + 10, '<br>';
echo $e + 10, '<br>';
// 整数15转为字符串'15'
echo $e . $f;

echo '<hr>';

// 强制转换

// 转换提示符: (int),(sgring)...
// (int)将$d强制转为整数,不再有警告信息
echo (int) $d + 10, '<br>';
// intval()转整数
echo intval($d) + 18, '<br>';
// strval($f)转字符串
echo strval($f) . ' hello', '<br>';

// 以上通过提示符和函数完成的强制转换,并不改变变量原始类型
// $f 依然是整数类型:integer
echo gettype($f), '<br>';

// settype()可将变量类型永久转换
settype($f, 'string');
// $f 永久的成为字符串类型
echo gettype($f), '<br>';

2.7 作用域

  • 变量作用域,也叫"变量范围", 即定义变量时的上下文环境
  • 变量作用域,通俗的说,就是变量的生效范围
  • 一个变量必定属于一个作用域, 这个作用域也包括了当前作用域中引入其它文件
  • 也有不受作用域限制的变量,例如超全局变量, 在程序中任何地方都是有定义的
  • 函数作用域: php 中只有函数可以创建作用域, 函数之外的代码全部在全局空间中
序号 作用域 描述
1 函数作用域 使用function关键字创建的作用域
2 全局作用域 函数之外的变量生效范围
  • php 中没有块作用域的概念, 这与其它编程语言不同, 请留意
  • 根据作用域不同, 变量可以分为三类:
序号 变量类型 描述
1 私有变量 函数中定义的变量
2 全局变量 函数之外定义的变量
3 超全局变量 也叫预定义变量,访问不受作用域限制
  • 超全局变量,也叫超全局数组,随系统加载,因此在所有脚本中均有定义,全局和函数中都可以访问
序号 变量名 描述
1 $GLOBALS 引用全局作用域中可用的全部变量
2 $_SERVER 服务器和执行环境信息
3 $_GET HTTP GET 请求:通过 URL 参数传递给当前脚本的变量的数组
4 $_POST HTTP POST 请求: 将变量以关联数组形式传入当前脚本
5 $_FILES HTTP 文件上传变量,保存着上传文件的全部信息
6 $_COOKIE 通过 HTTP Cookies 方式传递给当前脚本的变量的数组
7 $_SESSION 当前脚本可用 SESSION 变量的数组
8 $_REQUEST 默认情况下包含了 $_GET$_POST$_COOKIE 的数组
9 $_ENV 通过环境方式传递给当前脚本的变量的数组

示例代码: demo9.php

# 变量作用域

// 全局作用域: 默认
$siteName = 'php中文网';

// 函数作用域
function getInfo(): string
{
    // 函数中不能直接访问全局变量
    // $private = $siteName;

    // 方法1: global 引入
    global $siteName;
    $local = $siteName;

    // 访问2: 使用超全局变量: $GLOBALS
    $local = $GLOBALS['siteName'];

    // 函数中访问超全局变量
    echo $_SERVER['SCRIPT_NAME'] . '<br>';

    return $local;
}

// 全局中访问函数私有变量: 未定义错误
echo getInfo(), '<br>';

// 全局中访问超全局变量
echo $_SERVER['SCRIPT_NAME'];

/*
/demo9.php
php中文网
/demo9.php
*/

2.8 静态变量

2.8.1 基本常识

  • 定义在函数中的静态变量使用static修饰,并且与函数作用域绑定
  • 静态变量定义时必须初始化,且只能初始化一次,默认为0
  • 当程序执行离开函数作用域后,静态变量的值不丢失
  • 静态变量的值,可以在函数的多次调用中保持不变,即可带入下次调用中
  • 函数中静态变量遵循私有变量约束, 全局不可访问

2.8.2 应用场景

  • 当多次调用同一函数,且要求每次调用之间共享或保留某些变量的时候
  • 尽管全局变量也可以办到,但没必要, 采用局部静态变量更合适

示例代码: demo10.php


# 静态变量

namespace ns1;

function test1(): float
{
    // $sum: 局部动态变量,每次调用都会初始化,无法在多次调用中保持不变
    $sum = 0;
    $sum = $sum + 1;
    return  $sum;
}

echo test1(), '<br>';
echo test1(), '<br>';
echo test1(), '<br>';

echo '<hr>';

namespace ns2;

// 全局变量
$sum = 0;
function test1(): float
{
    // 通过全局变量,将每次的调用结果保存到全局中
    global $sum;
    $sum = $sum + 1;
    return  $sum;
}

echo test1(), '<br>';
echo test1(), '<br>';
echo test1(), '<br>';

echo '<hr>';

namespace ns3;


function test1(): float
{
    // 静态变量: 仅第一次调用时初始化,以后调用可保持原值
    // 静态变量: 可简单理解为仅在函数中使用的"伪全局变量"
    // 可以实现在函数的多次调用中的数据共享
    static $sum = 0;
    $sum = $sum + 1;
    return  $sum;
}

echo test1(), '<br>';
echo test1(), '<br>';
echo test1(), '<br>';

2.9 变量过滤器

  • PHP 过滤器用于验证和过滤来自非安全来源的外部数据
  • 外部数据来源:
序号 数据来源 描述
1 表单 来自表音的用户输入数据
2 Cookies 来自浏览器中的 cookie
3 服务器变量 防止伪装的合法访问
4 Web 服务数据 Web 请求的数据
5 数据库查询结果 数据表中的数据并不可信
  • 常用的过滤器函数
序号 函数 描述
1 filter_list()
2 filter_id()
3 filter_var() 过滤单个变量
4 filter_var_array() 过滤多个变量
5 filter_has_var() 检测是否存在某个外部变量
6 filter_input() 过滤单个外部变量
7 filter_input_array() 过滤多个外部变量
  • 外部变量类型: INPUT_GET, INPUT_POST, INPUT_COOKIE, INPUT_SERVER, INPUT_ENV
  • 过滤器主要分为二类: 验证过滤器, 清理过滤器

2.9.1 验证过滤器常量

  • 验证过滤器: 又叫"验证器", 主要用于数据的类型和格式验证
序号 过滤器函数 描述
1 FILTER_VALIDATE_INT 验证整数
2 FILTER_VALIDATE_FLOAT 浮点点验证
3 FILTER_VALIDATE_BOOLEAN 验证布尔项
4 FILTER_VALIDATE_EMAIL 验证邮箱
5 FILTER_VALIDATE_URL 验证 URL 地址
6 FILTER_VALIDATE_IP 验证 IP 地址
7 FILTER_VALIDATE_REGEXP 正则验证
  • FILTER_VALIDATE_BOOLEAN: 布尔选项的返回值类型
序号 返回值 描述
1 true "1", "true", "on" 和 "yes"
2 false "0", "false", "off", "no", ""
3 null 除以上情形外

2.9.2 清理过滤器常量

  • 清理过滤器: 去掉非法字符,仅保留指定内容
序号 过滤器函数 描述
1 FILTER_UNSAFE_RAW 保持原始数据
2 FILTER CALLBACK 自定义函数过滤数据
3 FILTER_SANITIZE_STRING 去除标签以及特殊字符:strip_tags()
4 FILTER_SANITIZE_STRIPPED "string" 过滤器别名
5 FILTER_SANITIZE_ENCODED URL-encode 字符串,去除或编码特殊字符
6 FILTER_SANITIZE_SPECIAL_CHARS HTML 转义字符, 等价于 htmlspecialchars()
7 FILTER_SANITIZE_EMAIL 仅保留邮箱地址的合法字符
8 FILTER_SANITIZE_URL 仅保留合法的 URL, 必须从协议开始http/https
9 FILTER_SANITIZE_NUMBER_INT 仅保留合法的数字和正负号+-
10 FILTER_SANITIZE_NUMBER_FLOAT 仅保留合法的数字和正负号+- 以及指数 .,eE
11 FILTER_SANITIZE_MAGIC_QUOTES 等价于函数: addslashes()

2.9.3 选项与标志

  • 可以对过滤器设置选项和标志, 对数据进行更加精准的过滤处理
  • 选项与标志使用数组键名表示: 'options'=>[...], 'flags'=>...(复数)
  • 举例:
filter_var($int, FILTER_VALIDATE_INT, ['options'=>['min_range'=>10,'max_range'=>80]]);
// FILTER_REQUIRE_SCALAR: 必须是标量(即单值数据,如字符串,数值,布尔, 不能是数组或对象)
filter_var($data, FILTER_VALIDATE_EMAIL, ['flags'=>FILTER_REQUIRE_SCALAR]);

示例代码: demo11.php

# 变量过滤器

// filter_list(): 查看支持的所有过滤器
// filter_id(): 返回过滤器常量对应的ID
foreach (filter_list() as $filter) {
    // echo $filter . ' => ' . filter_id($filter) . '<br>';
}
echo '<hr>';

// 1. filter_var(): 过滤单个变量
$age = 30;
var_dump(filter_var($age, FILTER_VALIDATE_INT));
// 验证时,会将变量值转为字符串类型, 所以这样写也对
$age = '30';
var_dump(filter_var($age, FILTER_VALIDATE_INT));
echo '<br>';
// 还可以添加第三个参数,对过滤器行为进行限定
// 被过滤的数据也支持字面量,但不推荐这样
var_dump(filter_var(10, FILTER_VALIDATE_INT, ['options' => ['min_range' => 18, 'max_range' => 60]]));
$age = 40;
var_dump(filter_var($age, FILTER_VALIDATE_INT, ['options' => ['min_range' => 18, 'max_range' => 60]]));

echo '<br>';

// 既可以使用过滤器常量,也可以使用过滤器ID
$email = 'admin@php.cn';
// 过滤器常量
var_dump(filter_var($email, FILTER_VALIDATE_EMAIL));
echo '<br>';
// 过滤器ID
var_dump(filter_var($email, 274));
echo '<br>';
var_dump(filter_var('peter@qq.com', 274));
echo '<br>';

// 2. filter_var_array():过滤多个变量
$a = 10;
$b = 'php';
// 返回值是数组, 验证失败返回false,成功返回原值
var_dump(filter_var_array([$a, $b], FILTER_VALIDATE_INT));
echo '<br>';
// 对于多变量验证最好将数组放在数组中统一处理
$data = [$a, $b, 'html', [6, 7, 8], 150, 200];
var_dump(filter_var_array($data, FILTER_VALIDATE_INT));
// 过滤掉验证未通过的元素
var_dump(array_filter(filter_var_array($data, FILTER_VALIDATE_INT)));

echo '<hr>';

// 3. filter_has_var(): 检测是否存在指定类型的外部变量
// 变量类型仅限:INPUT_GET, INPUT_POST, INPUT_COOKIE, INPUT_SERVER, INPUT_ENV
var_dump(filter_has_var(INPUT_GET, 'id'));
echo '<br>';

//4. filter_input(): 通过名称获取特定的外部变量,并且可以通过过滤器处理它
// 变量类型仅限:INPUT_GET, INPUT_POST, INPUT_COOKIE, INPUT_SERVER, INPUT_ENV
// false:验证失败, null: 变量不存在, 成功返回当前值
// $_GET['id'] 必须是大于1的整数
$res = filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT, ['options' => ['min_range' => 1]]);
var_dump($res);
echo '<hr>';

// 5. filter_input_array(): 同时验证多个外部变量
// 为一组数据应用统一过滤器
var_dump(filter_input_array(INPUT_GET, FILTER_SANITIZE_STRING));

// 为每一个元素应用不同的过滤器
// 设置变量对应过滤器
$args = [
    'username' => FILTER_SANITIZE_STRING,
    'email' => FILTER_VALIDATE_EMAIL,
    'age' => ['filter' => FILTER_VALIDATE_INT, 'flags'=>FILTER_REQUIRE_SCALAR, 'options' => ['min_range' => 18]],
    'blog' => FILTER_VALIDATE_URL,
];

// 非法: demo11.php?username=<a>admin</a>&email=abc&age=15&blog=blog.php.cn
// 合法: demo11.php?username=admin&email=abc@qq.com&age=25&blog=http://blog.php.cn
var_dump(filter_input_array(INPUT_GET, $args));

3. 细说常量

3.1 常量的特征

序号 特征
1 常量前面没有美元符号$
2 常量创建时必须初始化
3 常量禁止更新和删除
4 常量不受作用域限制
5 推荐使用大写字母加下划线命名

3.2 常量函数/关键字

序号 定义方式 描述
1 get_defined_constants() 查看系统所有常量
2 defined() 检测常量是否存在
3 define() 创建常量
4 const 关键字 创建常量
5 constant() 获取常量值
  • get_defined_constants(true): 常量分组打印,自定义常量在user分组
  • defined(): 返回布尔值
// 获取系统定义的所有常量
print_r(get_defined_constants(true));
// 仅查看用户自定义的常量
print_r(get_defined_constants(true)['user']);

3.3 创建常量

序号 定义方式 区别
1 define() 除了不能创建类常量, 可以在程序的任何地方定义
2 const 关键字 必须全局定义,不能用在函数和流程控制中,允许创建类常量
  • 常量值: int, float, string, boolean,null, array(php7+)
  • define($name, true): 允许忽略常量名大小写
  • const在编译阶段处理, define()在运行阶段处理,所以const必须定义在全局才有效
  • class类声明在编译时处理,且并不会创建作用域,当然也是全局中, 所以const可以用

3.4 获取常量

序号 函数/关键字 区别
1 echo 通常使用已知的常量名
2 constant() 常量名不确定或在变量中

示例代码: demo12.php


# 常量

// define():函数定义常量
define('LECTURE', '朱老师');
// const关键字: 定义常量
const COURSE = 'PHP';

// 常量不受作用域限制
function test1()
{
    echo LECTURE . '教: ' . COURSE . '<br>';
    // define()可以用在函数中,流程控制中,const不可以
    define('SEX', '男');
    echo SEX, '<br>';
    // const不能用在函数中
    // const AGE = 30;
    // echo AGE;
}

test1();
echo  '<hr>';


if (true) {
    // define()允许用在流程控制中
    define('EMAIL', 'admin@php.cn');
    echo EMAIL;
    // const 不能用在流程控制中
    // const B = 'hello world';
    // echo B;
}

echo '<hr>';

// const可以创建类常量,define()不行
class Demo
{
    const HELLO = 'php.cn';
    // define('HI', 'Peter Zhu');
}

echo Demo::HELLO, '<br>';

// 检测常量是否存在?
var_dump(defined('EMAIL'));

echo '<hr>';

// 获取系统定义的所有常量
// echo '<pre>' . print_r(get_defined_constants(), true) . '</pre>';
// 根据键名分组查看
// echo '<pre>' . print_r(get_defined_constants(true), true) . '</pre>';
// 仅查看用户自定义的常量
echo '<pre>' . print_r(get_defined_constants(true)['user'], true) . '</pre>';

// 常量的值允许是标量(单值)和数组
// 标量: 整数,浮点数, 字符串, 布尔,前面已有介绍
// 从php7.0+开始, 常量也支持数组类型了
const DB_LINKS = [
    'host' => 'localhost',
    'username' => 'root',
    'password' => 'root',
    'charset' => 'utf8',
];

echo '<pre>' . print_r(DB_LINKS, true) . '</pre>';

echo '<hr>';

// 除了用echo 打印常量外, 还可以用constant()函数
// constant参数是字符串,字符串的内容是变量名,并且只返回的值,必须用echo / print_r等打印
echo constant('LECTURE') . '<br>';
// 你可能会认为这样做多此一举,因为完全可以直接用echo, 事实也的确如此
echo LECTURE . '<br>';

// 但是,当我们不知道常量名, 或者常量名在变量中时, 这个函数就非常有用了
// 常量名在变量中
$constName = 'EMAIL';
// 只能输出常量名,不能获取到常量值
echo $constName, '<br>';
// constant(): 可以将常量名从变量中解析来正确读到
echo constant($constName), '<br>';
// 空字符串也能做常量名, 尽管没啥意义,但却是合法的(我想应该没人会这样做)
define('', "其实我也是常量");
// 空字符串不是一个有效的php标识符,所以不能使用const来定义这个常量
// 下面指令肯定获取不到常量值
echo '', '<br>';
// 而constant()函数仍然可以读到常量, 不知道这是不是一个Bug?
echo constant('');

3.5 预定义常量

预定义常量非常多,有许多与具体扩展相关,如 PDO, 这里仅列出系统级常用的:

序号 预定义常量 描述
1 PHP_VERSION PHP 版本
2 PHP_MAXPATHLEN PHP 路径最大长度:1024
3 PHP_OS_FAMILY 操作系统:Windows/Darwin/Linux
4 PHP_SAPI web 服务器与 php 之间接口: apache2handler
5 PHP_EOL 行尾结束符
6 PHP_INT_MAX 最大整数: 9223372036854775807
7 PHP_INT_MIN 最小整数: -9223372036854775808
8 PHP_INT_SIZE 整数宽度: 8
9 PHP_FLOAT_MAX 最大浮点数:1.7976931348623E+308
10 PHP_FLOAT_MIN 整小浮点数: 2.2250738585072E-308
11 DEFAULT_INCLUDE_PATH 默认 PHP 命令路径
12 PHP_EXTENSION_DIR 默认 PHP 扩展路径
13 E_ERROR 运行时错误: 致命中断
14 E_PARSE 语法解析错误: 致命中断
15 E_NOTICE 运行时提示: 不中断
16 E_WARNING 运行时警告: 不中断
17 E_ALL 所有级别错误(除E_STRICT)
18 E_STRICT 更加严格的错误处理机制,高于E_ALL
19 TRUE 布尔真
20 FALSE 布尔假
21 NULL
22 DIRECTORY_SEPARATOR 目录分隔符

更多预定义常量:www.php.net/manual/zh/r…

3.6 魔术常量

  • 魔术常量也属于"预定义常量", 比较特殊所有单独列出
  • 所谓"魔术", 是指常量的值, 会随它们在代码中的位置改变而改变
  • 魔术常量不区分大小写, 但是推荐全部大写
序号 魔术常量 描述
1 __LINE__ 文件中的当前行号
2 __FILE__ 文件的完整路径和文件名
3 __DIR__ 文件所在目录
4 __FUNCTION__ 当前的函数名称
5 __CLASS__ 当前类名称
6 __TRAIT__ 当前Trait名称
7 __METHOD__ 当前类方法名称
8 __NAMESPACE__ 当前命名空间名称

示例代码: demo13.php


# 预定义常量

echo '版本号: ' . PHP_VERSION . '<br>';
echo '操作系统: ' . PHP_OS_FAMILY . '<br>';
echo '最大整数: ' . PHP_INT_MAX . '<br>';
echo '最大浮点数: ' . PHP_FLOAT_MAX . '<br>';
echo '目录分隔符: ' . DIRECTORY_SEPARATOR . '<hr>';

// 魔术常量

echo '当前行号: ' . __LINE__ . '<br>';
echo '当前文件: ' . __FILE__ . '<br>';
echo '当前目录: ' . __DIR__ . '<br>';
// 更多魔术常量在类与对象中再详细介绍

3.7 常量命名空间

  • 当使用的第三方组件(类库)中存在也当前脚本命名冲突的常量名时,可以用命名空间解决
  • 命名空间允许将同名的标识符,定义在不同的空间中,类似同名文件可存放在不同目录下
  • 命名空间使用关键字namespace声明, 必须放在脚本的首行,且前面不允许有任何输出
# 常量的命名空间

// 报重复声明错误
// const APP_PATH = __DIR__ . '/public/';
// const APP_PATH = __DIR__ . '/home/';

// 将同名常量定义在不同的空间中, 则不会报错
namespace ns1 {
    const APP_PATH = __DIR__ . '/public/';
}

namespace ns2 {
    const APP_PATH = __DIR__ . '/home/';
}

// 全局空间,不需要空间名
namespace {
    // 访问命名空间中的常量, 必须带上空间名称
    echo \ns1\APP_PATH . '<br>';
    echo \ns2\APP_PATH . '<br>';
}

4. 细说运算符

4.1 算术运算符

序号 运算符 示例 描述
1 + $a+$b a与b 的和
2 - $a-$b a与b 的差
3 * $a*$b a与b 的积
4 / $a/$b a与b 的商
5 % $a%$b a与b 的余(模)
6 ++ $a++/++$a 先用再加/先加再用
7 -- $a--/--$a 先用再减/先减再用
8 ** 2**4 幂运算:2 的 4 次方
  • $a++=>$a = $a + 1, $a--=>$a = $a - 1
  • **: 幂运算需要 php5.6+版本

4.2 字符串运算符

运算符 示例 描述
. $str1 . $str2 连接运算,返回连接之后的字符串

4.3 赋值运算符

序号 运算符 示例 描述
1 = $a = 10 将表达式的值赋给变量
2 += $a += 10 $a = $a + 10;
3 -= $a -= 10 $a = $a - 10;
4 *= $a *= 10 $a = $a * 10;
5 /= $a /= 10 $a = $a / 10;
6 %= $a %= 10 $a = $a % 10;
7 .= $a .= 'a' $a = $a . 'a';

4.4 比较运算符

序号 运算符 示例 描述
1 > $a > $b 大于
2 >= $a >= $b 大于或等于
3 < $a < $b 小于
4 <= $a <= $b 小于或等于
5 == $a == $b 等于 (值相等即可)
6 === $a == $b 全等于(二边值与类型全等)
7 != $a != $b 不相等
8 !== $a !== $b 不全等
9 <=> $a <=> $b 太空船php7+
  • 比较运算符: 返回布尔值,常用于流程控制中(if/while/for...)
  • <=>: 也叫组合比较符, 例如$a <=> $b, 有三种返回结果
序号 运算 结果
1 $a > $b 返回>0的整数
2 $a < $b 返回<0的整数
3 $a = $b 返回=0的整数

示例代码: demo15.php

// 比较运算符返回布尔值
var_dump(10 > 20);
// `==`二边值类型不同时,会自动转换,值相等返回true
var_dump('10' == 10);
// `===`: 不发生转换,要求值与类型都相同才返回true
var_dump('10' === 10);
// 结果: bool(false) bool(true) bool(false)

echo '<hr>';

# 太空船运算符
var_dump(10 <=> 20);
var_dump(10 <=> 5);
var_dump(10 <=> 10);
// 结果: int(-1) int(1) int(0)

4.5 逻辑运算符

序号 运算符 示例 描述
1 && $a && $b 逻辑与
2 || $a || $b 逻辑或
3 ! !$a 逻辑非
4 xor $a xor $b 逻辑异或
  • 逻辑运算符二边表达式必须是布尔类型
  • 对应别名: && => and, || => or, ! => not
  • 优先级: ! > && > ||
  • 异或: 当二边表达式运算结果不相同时,结果为true

示例代码: demo16.php

error_reporting(E_ALL ^ E_NOTICE);
// 逻辑运算符

// && 二边全为true,结果为true
var_dump((3 < 4) && (5 > 8));
// && 二边只要有一个为true,结果为true
var_dump((3 < 4) || (5 > 8));

// 当$name存在且值等admin时显示欢迎词
// 未定义的变量返回false
if ($name && $name === 'admin') {
    echo '欢迎您';
} else {
    echo '未登录';
}

4.6 三元运算符

序号 运算符 示例 描述 场景
1 ?: $a ?: $b 仅判断表达式true/false 简化双分支
2 ?? $a ?? $b 判断变量存在且不为 null 设置默认值
  • 其实??不算三元运算符: 准确讲应该是NULL合并符
  • ??: 需要满足二个条件(1)变量存在(2)且值不为null,其实就是isset()的要求

示例代码: demo17.php

// ?: 简化双分支
$price = 1200;
if ($price > 1000) {
    echo '太贵了';
} else {
    echo '真便宜';
}

// 三元运算符
echo $price > 1000 ? '太贵了' : '真便宜';

echo '<hr>';

// ?? null合并运算符,返回表达式
// isset($a): 变量存在定义且值不为NULL, 才返回true
echo isset($password) ? $password : 'root';

// 如果$pass变量存在且值不为null,则使用该变量,否则创建该变量并设置默认值
// $password = '123';
$password =  $password ?? 'root';
echo $password;

4.7 错误屏蔽符

运算符 示例 描述 场景
@ @(10/0) 屏蔽所有运行错误 线上生产环境

示例代码: demo18.php


// @错误屏蔽符, 也叫 错误抑制符
// @只对表达式有效
// 实际开发中, 应该将这些错误写到日志文件中
// 变量未定义错误
echo @$username;

// INF: 无穷大
// echo @(10 / 0);
@(10 / 0);

// 语法错误等致命错误是无法屏蔽的
// @print_r($a)

@include('hello.php');

5. 流程控制

5.1 分支结构

序号 类型 语法 模板语法
1 单分支 if(){...} if(): ... endif;
2 双分支 if(){...} else {...} if(): ... else: ... endif;
3 多分支 if(){...} elseif {...} else {...} if(): ... elseif: ... else: ... endif;
4 switch switch() {case: ... break...} switch(): ...endswitch;

示例代码: demo19.php

# 分支结构

// 购买金额
$amount = 5500;
// 实际支付
$payment = $amount;

// 单分支
if ($amount > 5000) {
    $payment = $amount * 0.9;
}
// 模板语法
if ($amount) :
    $payment = $amount * 0.9;
endif;

echo '实际支付: ' . $payment . '<br>';

echo '<hr>';

// 双分支
$amount = 3500;

if ($amount > 5000) {
    $payment = $amount * 0.9;
} else {
    $payment = $amount;
}

// 模板语法
if ($amount > 5000) :
    $payment = $amount * 0.9;
else :
    $payment = $amount;
endif;

echo '实际支付: ' . $payment . '<br>';

echo '<hr>';

// 多分支
$amount = 13500;

if ($amount > 5000 && $amount < 10000) {
    $payment = $amount * 0.9;
} elseif ($amount >= 10000 && $amount < 20000) {
    $payment = $amount * 0.7;
} elseif ($amount >= 20000 && $amount < 30000) {
    $payment = $amount * 0.5;
} else {
    $payment = $amount;
}

// 模板语法
if ($amount > 5000 && $amount < 10000) :
    $payment = $amount * 0.9;
elseif ($amount >= 10000 && $amount < 20000) :
    $payment = $amount * 0.7;
elseif ($amount >= 20000 && $amount < 30000) :
    $payment = $amount * 0.5;
else :
    $payment = $amount;
endif;


echo '实际支付: ' . $payment . '<br>';

echo '<hr>';

// switch分支
$amount = 23500;
switch (true) {
    case $amount > 5000 && $amount < 10000:
        $payment = $amount * 0.9;
        break;
    case $amount >= 10000 && $amount < 20000:
        $payment = $amount * 0.7;
        break;
    case $amount >= 20000 && $amount < 30000:
        $payment = $amount * 0.5;
        break;
    default:
        $payment = $amount;
}

// 模板语法
$amount = 8500;
switch (true):
    case $amount > 5000 && $amount < 10000:
        $payment = $amount * 0.9;
        break;
    case $amount >= 10000 && $amount < 20000:
        $payment = $amount * 0.7;
        break;
    case $amount >= 20000 && $amount < 30000:
        $payment = $amount * 0.5;
        break;
    default:
        $payment = $amount;
endswitch;
echo '实际支付: ' . $payment . '<br>';

// switch最常用的场景是单值判断
// 例如,根据不同折扣输出不同金额
$discount = 0.8;

switch ($discount):
    case 0.9:
        $payment = $amount * $discount;
        break;
    case 0.7:
        $payment = $amount * 0.7;
        break;
    case 0.5:
        $payment = $amount * 0.5;
        break;
    default:
        die('不支持的折扣率');
endswitch;

echo '实际支付: ' . $payment .  ', 折扣率: ' . $discount;

5.2 循环结构

序号 类型 语法 模板语法
1 入口判断型 while(){...} while(): ... endwhile;
2 出口判断型 do {...} while(); do : ... while();
3 计数型 for(){...} for () : ... endfor;

循环控制关键字:

序号 关键字 描述
1 continue 终止本次循环,提前进入下一轮
2 break 跳出本层循环

示例代码: demo20.php

<?php
# 循环结构

$cities = ['武汉', '合肥', '南京', '杭州', '上海'];

// 1. 入口判断型
// 只要循环条件为真,则循环执行
// current(): 获取当前数组元素的值, next():移动数组指针指向下一条记录
while ($city = current($cities)) {
    echo $city . '<br>';
    // 更新循环条件: 读到下一个数据
    next($cities);
}

// 数组指针复位,指向第一个元素
reset($cities);

// 模板语法
while ($city = current($cities)) :
    echo $city . '<br>';
    // 更新循环条件: 读到下一个数据
    next($cities);
endwhile;

echo '<hr>';

// 2. 出口判断型
reset($cities);

// 只要循环条件为真,则循环执行
do {
    echo $city . '<br>';
    // 更新循环条件: 读到下一个数据
    next($cities);
} while ($city = current($cities));
// 你会发现,第一个城市"武汉"消失了,为什么呢?
// 因为出口判断型,无论条件是否满足,循环体总会先执行一遍,next($cities)跳过了第一个元素

// do-while 没有对应的模板语法

echo '<hr>';

// 3. 计数型
reset($cities);

// for()循环是while()的聚合版,将循环条件与条件更新全部集成到了参数中
// count(): 获到数组元素数量
for ($i = 0; $i < count($cities); $i++) {
    echo $cities[$i] . '<br>';
}

// 模板语法
reset($cities);

for ($i = 0; $i < count($cities); $i++) :
    echo $cities[$i] . '<br>';
endfor;

echo '<hr>';

// 关键字continue: 终止本次循环,提前进入下一轮
// 关键字break: 跳出本层循环
$cities = ['武汉', '合肥', false, '南京', null, '杭州', '上海'];
// 其实用while()遍历数组时是有一个bug的,如果数组存在计算结果为false的元素会提前结束循环
while ($city = current($cities)) :
    echo $city . '<br>';
    next($cities);
endwhile;

// continue: 跳过运算结果为false数据
reset($cities);

// 因为while()条件为false时, 不会执行,所以换成for
for ($i = 0; $i < count($cities); $i++) :
    if (!$cities[$i]) continue;
    echo $cities[$i] . '<br>';
    // 如果值为"杭州"跳出循环
    if ($cities[$i] === '杭州') break;
endfor;