Web开发——PHP入门学习笔记
本文最后更新于2 天前,其中的信息可能已经过时,如有错误请发送评论

前言

这段时间学习刷题都很努力,ctfhub 的技能树 Web 方面的题目也就剩下一个 SSRF 了,本来想着一起速通后再去看相关的视频仔细学习,结果前面的都还好,就算碰到不会的看看别人的 WP 也能做出来,结果到 SSRF Post 请求的时候卡住了,看了两篇 WP 都没有做出来,一篇还是非常详细的文章,还是不懂,于是我打算看视频学习,不过视频中有从白盒的角度发现 SSRF 漏洞的部分,所以我打算在看之前学习一下 PHP 和 JS,因为我前面做题的时候就是有点没有和这个的基础,导致基本上是靠 ai 。顺便学习一下 python ,不过因为我之前学习过,只不过什么面向对象之类的细节没有学习,当时是看 B 站视频速通的。不过因为时间关系,我打算在24日之前把 SSRF 的分支做完,就不太够我学习 python 了,PHP 也是,打算先把语言简单学一下,然后后面有时间再进阶,所以这篇文章是入门学习笔记

因为是速通,所以我只打算最多花两天时间,再加上 PHP 是服务端语言,为了方便我直接使用的雨云的服务器,试用两天可比买一个月划算,然后就开了个服务器,文件本地写了直接放到服务器上访问

本篇教程是基于菜鸟教程和Youtube的PHP入门教程

PHP简介

PHP(全称:PHP:Hypertext Preprocessor,即”PHP:超文本预处理器”)是一种通用开源脚本语言,PHP 脚本在服务器上执行

PHP语法

<?php 开头 ?> 结尾,每句话用 ; 号结尾,分号是一种分隔符,用于把指令集区分开来,PHP 有两种在浏览器中输出的基础指令,echoprint

<!DOCTYPE html>
<html>
<body>

<h1>My first PHP page</h1>

<?php
 // PHP 代码写在 <?php 和 ?> 中间
	echo "Hello World!";
?>

</body>
</html>

直接将如上代码放到服务器上执行发现 echo 没有输出 Hello World! 这是因为注释中的 ?> 被解析为结束了,所以后面的内容直接被当成 html 输出,所以我们需要用到多行注释或者将内容转义,避免出现该错误

PHP注释

在 PHP 中有三种注释方法,分别是上面代码块中使用的 // 单行注释,# 单行注释,/* ... */ 多行注释,以及 /** ... */ 文档注释,文档注释是多行注释的特殊形式,因为它遵循特定的语法规范,可以被各种开发工具识别和解析,所以它被用于生成 API 文档,适合描述类、函数等

<?php
 // 这是单行注释
 #  这也是单行注释
 /* 这是多行注释
 注释内容
 */
 /** 这是特殊的多行注释
    * @param string $name 用户姓名
    * @return string
    * 也叫文档注释
    */
    function sayHello($name) {
    	return "Hello, $name!";
}
?>

现代 IDE(如 PhpStorm、VS Code)可以解析 PHPDoc (文档注释) ,提供代码自动补全,参数类型提示,错误检查,快速文档查看

工具如 phpDocumentor 可以根据 PHPDoc 自动生成 HTML 文档,工具如 PHPStan、Psalm 使用 PHPDoc 进行类型检查,PHP 文档注释不仅提高了代码的可读性,还为团队协作和工具集成提供了重要支持

<!DOCTYPE html>
<html>
<body>

<h1>My first PHP page</h1>

<?php
    // PHP 代码写在 <?php 和 ?\> 中间 \ 转义,否则会被解析为结束
	echo "Hello World!";
?>

</body>
</html>

再次访问这个 php 文件就会发现浏览器正常输出了一级标题 My first PHP pageHello World!

PHP变量

变量概述

变量是用于存储信息的 “容器”

<?php
	$x=5;
	$y=6;
	$z=$x+$y;
	echo $z;
?>

如上会输出变量 z ,也就是数字 11 (变量x和变量y相加的结果)

PHP变量与代数类似,可以给 PHP 变量赋予某个值(x=5)或者表达式(z=x+y)

变量可以是很短的名称(如 x 和 y)或者更具描述性的名称(如 age、carname、totalvolume),不过有对应的规则:

变量必须以 $ 符号开始,后面紧跟着变量名称,变量名必须以字母或者下划线字符开始,且变量名只能包含字母、数字以及下划线(A-z、0-9 和 _ ),不能包含空格,也要注意大小写,因为变量名是区分大小写的,例如 $y$Y 就是两个不同的变量

PHP 和 Python 一样,是没有变量声明的命令,变量是在第一次赋值的时候才会被创建,如下:

<?php
 $txt="Hello World!";
 $x=5;
 $y=10.5;
?>

在上面的语句中,变量 txt 将保存值 Hello World!,且变量 x 保存值 5,y 保存值 10.5,当你赋值一个文本(字符串)给变量时,请在文本值两侧加上引号

因为 PHP 是一门弱类型的语言,所以我们可以发现,不用向 PHP 声明该变量的数据类型,PHP 会根据变量的值来自动把变量转换为正确的数据类型,在强类型的编程语言中,我们必须在使用变量前先声明(定义)变量的类型和名称

变量作用域

变量的作用域是脚本中变量可被引用/使用的部分,PHP 有四种不同的变量作用域:

local(局部变量)、global(全局变量)、static(静态变量)、parameter(参数变量)

局部和全局作用域

在所有函数外部定义的变量,拥有全局作用域。除了函数外,全局变量可以被脚本中的任何部分访问,要在一个函数中访问一个全局变量,需要使用 global 关键字,在 PHP 函数内部声明的变量是局部变量,仅能在函数内部访问

这一点就跟 python 很像了,不过 python 读取全局变量时不需要使用 global 关键字,除非需要修改才会用到,而 PHP 需要:

<?php
 $x=5; // 全局变量
 function myTest() {
     $y=10; // 函数内部,局部变量
     echo "<p>测试函数内变量<p>";
     echo "变量 x 为:$x";
     echo "<br>";
     echo "变量 y 为:$y";
 }

	myTest();

 echo "<p>测试函数外变量<p>";
 echo "变量 x 为:$x";
 echo "<br>";
 echo "变量 y 为:$y";
?>

访问服务器查看页面:

可以看到在函数内部的全局变量 x 报错了,因为没有使用 global,在函数外使用函数内的变量 y 也报错了,因为 y 是局部变量

global 关键字

global 关键字用于函数内访问全局变量,在函数内调用函数外定义的全局变量,我们需要在函数中的变量前加上 global 关键字

<?php
 $x=5;
 $y=10;

 function myTest() {
     global $x,$y;
     $y=$x+$y;
 }

 myTest();
 echo $y;
?>

因为函数里面使用的是全局变量,所以最后 echo 输出的变量 y 是函数里面计算的结果,也就是 15

内存中的变量:
全局作用域:
$x -> [内存地址1] -> 值: 5
$y -> [内存地址2] -> 值: 10
调用 myTest() 函数:
global $x, $y; 使得函数内的 $x 和 $y 指向相同的内存地址
函数内 $x -> [内存地址1] -> 值: 5
函数内 $y -> [内存地址2] -> 值: 10 (然后被修改为 15)
函数执行完毕:
全局 $y -> [内存地址2] -> 值: 15 (已经被修改)

PHP 将所有全局变量存储在一个名为 $GLOBALS[index] 的数组中。 index 保存变量的名称。这个数组可以在函数内部访问,也可以直接用来更新全局变量,比如上面这个例子就可以写成如下:

<?php
 $x=5;
 $y=10;

 function myTest() {
     $GLOBALS['y']=$GLOBALS['x']+$GLOBALS['y'];
 }

 myTest();
 echo $y;
?>

Static 作用域

基本概念:

静态变量在函数调用之间保持其值,不会在函数结束时被销毁

当一个函数完成时,它的所有变量通常都会被删除。然而,有时候您希望某个局部变量不要被删除,要做到这一点就需要使用 static 关键字进行声明,然后,每次调用该函数时,该变量将会保留着函数前一次被调用时的值,但是该变量仍然是函数的局部变量

<?php
 function myTest() {
     static $x=0;
     echo $x;
     $x++;
     echo PHP_EOL;
 }

 myTest();
 myTest();
 myTest();
?>

这段代码的结果是 0 1 2,$x++ 是 PHP 中的后置递增运算符(post-increment operator),它的基本含义如下:

$x++ 表示将变量 $x 的值增进 1,但表达式的返回值是递增前的值

<?php
$x = 5;
$y = $x++;  // 先赋值,再递增
echo "x = $x\n";  // 输出: x = 6
echo "y = $y\n";  // 输出: y = 5
?>

<?php
$x = 5;
$y = ++$x;  // 先递增,再赋值
echo "x = $x\n";  // 输出: x = 6
echo "y = $y\n";  // 输出: y = 6
?>

上面代码中 $y=$x++ 就是将 x 值赋值给 y,然后在改变 x 的值,下面的代码则是先改变 x 的值,然后再将改变后的值赋值给 y ,重新回到前面的代码,x 的值为 0,输出的结果就是 0 ,然后递增后的值为 1,第二次执行函数的时候输出的 x 的值就为 1 了,然后再次递增,下一次输出就是 2 ,因为我们事先用 static 将 x 变量转换为静态变量,即使函数执行完毕,也不会销毁,并且保留了值,所以再第二次执行函数时,它的值变成了递增后的值,不过它还是一个局部变量,也就是说只能在这个函数中使用,其他函数还是无法使用

参数作用域

参数 (Parameter) 是函数定义时声明的变量,用于接收调用函数时传递的值

参数是通过调用代码将值传递给函数的局部变量,参数是在参数列表中声明的,作为函数声明的一部分

<?php
 function myTest($x) {
     echo $x;
 }

 myTest(5);
?>

这一点就跟 python 一样,都是将参数直接传入,使用的时候指定参数就好

def print_text(text):
 print(text)

print_text('1231313')
print_text(input('请输入文本:\n'))

PHP输出

PHP中有四种输出语句,分别是前面用到的 echo 、print 以及 printf 、sprintf

echo()函数

echo() 函数可以一次性输出多个字符串、HTML 标记或变量,可以使用圆括号,也可以不使用圆括号,在实际应用中一般不使用圆括号。echo() 函数更像一条语句,无返回值

显示字符串:

<?php
 echo "<h2>php 很有趣</h2>";
 echo "hello world!<br>";
 echo "我要学习 PHP<br>";
 echo "这是一个","字符串,","使用了","多个","参数。<br>";
 echo "这是圆点"."拼接字符串<br>";
 echo ("hello ","world!<br>");    // 不能这样写,会报错
 echo ("hello "."world!<br>");
?>

这里的逗号可以将字符串拼接起来,当然使用 . 圆点也是可以的,不过如果在输出时使用了阔号包裹就不能使用逗号,否则会报错。不过使用逗号远比使用圆点要快,因为圆点是需要 PHP 先创建一个新的字符串来存储对象结果,然后再输出,逗号 echo 则是直接将每个参数分别输出,不需要创建临时的连接字符串

然后还有一点是我看别人文章说的,但是我将他的代码复制到我服务器上发现执行的都是正确的,可能是版本问题吧,我用的是 PHP 8 的版本,然后他说 echo 会先对每个参数进行计算,然后进行字符串连接,最后输出,所以使用逗号不会出现上述问题,但是我逗号和圆点执行的都没有问题,如果大家碰上了这个问题可以换个版本或者使用逗号试试

显示变量:

<?php
 $txt1="学习 PHP";
 $txt2="lawking.top";
 $cars=array("Volvo","BMW","Toyota");

 echo $txt1;
 echo "<br>";
 echo "在 $txt2 学习 PHP ";
 echo "<br>";
 echo "我车的品牌是 {$cars[0]}";
?>

如上,如果变量内容是数组,就需要使用大括号 {}

print()函数

print 同样是一个语言结构,可以使用括号,也可以不使用括号,但是 print 连接多个参数时需要使用圆点,如果使用逗号就会报错,并且 print 函数是有返回值的,其返回值为 1,当其执行失败时返回 0,显示字符串:

<?php
 print "<h2>php 很有趣</h2>";
 print "hello world!<br>";
 print "我要学习 PHP<br>";
 print "这是圆点"."拼接字符串<br>";
 print ("hello "."world!<br>");
?>

显示变量:

<?php
 $txt1="学习 PHP";
 $txt2="lawking.top";
 $cars=array("Volvo","BMW","Toyota");

 print $txt1;
 print "<br>";
 print "在 $txt2 学习 PHP ";
 print "<br>";
 print "我车的品牌是 {$cars[0]}";
?>

printf()函数

相比 echo 和 print 的输出,printf 函数则会对输出内容格式化,格式化字符串包括两部分内容,一部分是正常的字符串,将被直接输出,另一部分是格式化规定字符,以“%”开始,后跟一个或几个规定字符,用于确定输出内容格式

在使用格式化字符串的时候,后面跟着的参数列表必须和前面的一一对应,各个参数使用哦个逗号隔开

常用类型转换符如下:

%b整数转换为二进制数
%c整数转换为ASCII
%d整数转换为有符号十进制数
%f倍精度型转换为浮点型
%o整数转换为八进制数
%s整数转换为字符串
%u整数转无符号十进制数
%x整数转十六进制数(小写)
<?php
 printf("这本书%d元","20");
 echo "<br>";
 printf("这本书%f元","20");
 echo "<br>";
 echo printf("这本书%f元","20");
?>

执行结果:
这本书20元
这本书20.000000元
这本书20.000000元21

上面这段代码 %d 是期望转换为整数,因为20本来就是,所以结果不变,%f 是期望转换成浮点数,所以结果为 20.000000,第三个是输出了 printf 函数的返回值,printf 也是有返回值的,返回的内容是字符串长度,不过我不知道怎么数出来21的,不过的确返回的是字符串长度

sprintf()函数

printf() 函数的返回值是字符串长度,而 sprintf() 函数的返回值是字符串本身,因此,sprintf() 函数必须通过 echo() 函数才能输出,如果省略 echo 页面就不会有显示的内容

sprintf() 函数和 printf() 函数的用法和 C语言中的 printf() 函数非常相似,经常使用 sprintf() 函数将十进制数转换成其他进制数

<?php
 echo sprintf("%b","20");
?>

执行结果:
10100

PHP EOF(heredoc)

PHP EOF (heredoc) 是一种定界符语法,用于创建多行字符串,避免使用大量的引号和转义字符

是一种在命令行shell(如sh、csh、ksh、bash、PowerShell和zsh)和程序语言(像Perl、PHP、Python和Ruby)里定义一个字符串的方法,使用概述:

  • 必须后接分号,否则编译不通过
  • EOF 可以用任意其他字符代替,只需要保证结束标识与开始标识一致,且不在正文中出现
  • 结束标识必须定格独占一行,必须在行首,且前后不能有任何空格或字符,结束末尾必须带有分号
  • 开始标识可以不带引号或带单双引号,不带引号与带双引号效果一致,解释内嵌的变量和转义符号,带单引号则不解释内嵌的变量和转义符号
  • 当内容需要内嵌引号(单引号或双引号)时,不需要加转义符,本身对单双引号转义
// 解释上面第四点
<?php
    $name = "张三";
    $age = 25;
    $str = <<<EOF
    我的名字是:$name
    年龄:$age
    换行符:\n
    EOF;
    echo $str;
?>

<?php
    $name = "张三";
    $age = 25;
    $str = <<<"EOF"
    我的名字是:$name
    年龄:$age
    换行符:\n
    EOF;
    echo $str;
?>

执行结果:
我的名字是:张三
年龄:25
换行符:

如上,是标识符带双引号和不带引号的结果,如果带上单引号,输出如下:

我的名字是:$name
年龄:$age
换行符:\n

因为单引号不会解析变量和转义字符,只是字面量输出

第五点,Heredoc/Nowdoc最大的优势就是不需要转义引号,让代码更清晰易读,特别是在处理HTML、SQL等包含大量引号的内容时特别有用

位于开始标记和结束标记之间的变量可以被正常解析(单引号除外),但是函数不可以,并且变量不需要用连接符.,来拼接

EOF 中是会解析 html 格式内容的,并且在双引号内的内容也有转义效果

PHP数据类型

PHP 变量存储不同的类型的数据,不同的数据类型可以做不一样的事情,支持的类型如下:

  • String(字符串)
  • Integer(整型)
  • Float(浮点型)
  • Boolean(布尔型)
  • Array(数组)
  • Object(对象)
  • NULL(空值)
  • Resource(资源类型)

字符串就是一串字符序列,整型就是没有小数点的数字(可以是正数和负数,也可以是10/16/8进制的一串数字),浮点型那就是带有小数部分的数字或是指数形式,布尔型就是ture和flase,数组可以在一个变量中存储多个值,对象数据类型也可以用于存储数据,在php中,对象必须先使用 class 关键字声明,然后在类中定义数据类型,然后在实例化的类中使用数据类型,NULL值表示没有值,就是一个空值,也可以用来清空变量数据,php 资源 resource 是一种特殊变量,保存了到外部资源的一个引用,常见的资源数据类型有打开文件,数据库连接,图形画布区域等,由于资源类型变量保存有为打开文件、数据库连接、图形画布区域等的特殊句柄,因此将其他类型的值转换为资源没有意义

使用 var_dump 关键字可以查看前面的数据类型,使用 get_resource_type 函数可以返回资源类型

<?php
    $c = mysql_connect();
    echo get_resource_type($c)."\n";
    // 打印:mysql link

    $fp = fopen("foo","w");
    echo get_resource_type($fp)."\n";
    // 打印:file

    $doc = new_xmldoc("1.0");
    echo get_resource_type($doc->doc)."\n";
    // 打印:domxml document
?>

get_resource_type 此函数会返回一个字符串,用于表示传递给它的资源的类型,如果参数不合法就会报错

var_dump 此函数会判断一个变量的类型与长度,并输出变量的数值,如果变量有值,则输出是变量的值,并返回数据类型。显示关于一个或多个表达式的结构信息,包括表达式的类型与值。数组将递归展开值,通过缩进显示其结构

mysql_connect 这个函数从 PHP 7.0 开始就已经完全移除,这里只是让大家理解这个意思,如果真执行上面的代码会报错,需要修改这个函数,因为我用的是 PHP 8.0 就报错了

PHP类型比较

PHP 是一个弱类型的语言,但也需要明白变量类型以及它们的意义,因为我们经常需要对变量进行比较,PHP 中比较包括两种,分别是松散比较==和严格比较===,前者只比较值,不比较数据类型,后者除了比较值还会比较数据类型,例如 “42” 是一个字符串而 42 是一个整数,FALSE 是一个布尔值,而 “FALSE” 是一个字符串

<?php
 if(42 == "42") {
     echo '1、值相等';
 }

 echo PHP_EOL; // 换行符

 if(42 === "42") {
     echo '2、类型相等';
 } else {
     echo '3、类型不相等';
 }
?>

执行结果:
1、值相等
3、类型不相等

其他比较图

因为其他比较的内容有点多,不可能全部写下来,而且也不一定全用上,用上的时候点这个看一下就好了,或者记几个常用的就差不多了,可以直接点击上方这个跳转查看

PHP常量

PHP 中的常量是指一旦定义后其值不能被改变的标识符,常量值被定义后,在脚本的其他任何地方都不能被改变,常量可以用 define() 函数或 const 关键字来定义

常量的特性

  • 不变性,一旦被定义,其值就无法改变
  • 全局作用域,定义后整个脚本任何地方都可以使用,且不需要 global 关键字
  • 常量的值可以是标量数据类型或者数组
  • 常量区分大小写,如需定义大小写不敏感需再 define 函数的第三个参数设置为 true
  • 一个常量由英文字母、下划线、和数字组成,但数字不能作为首字母出现,常量不需要加 $

设置常量

使用 define() 函数,语法如下:

define(name, value, case_insensitive)

name 为常量的名称,value 是常量的值,case_insensitive 是大小写敏感的设置,不过自 PHP 7.3 开始,定义不区分大小写的常量已被弃用,从8.0开始只有 false 是可接受的值

<?php
 define("lawking","hacker_lawking");

 echo lawking;
?>

常量是全局的

如上,就可以设置一个常量,并且输出这个常量的值,这个常量因为是全局作用域,所以在函数或者任意地方都可以使用,如下:

<?php
 define("lawking","hacker_lawking");

 echo lawking;

 echo "<br>";

 function echo_lawking() {
     echo lawking;
 }

 echo_lawking()
?>

const 关键字

const 也可以用来定义常量,语法如下:

const 变量名 = 值;

例如:

<?php
 const site_url = "https://lawking.top";
 echo site_url;
?>

预定义常量

PHP 提供了一些预定义常量,可以在脚本中直接使用。这些常量通常用于获取 PHP 的配置信息、版本信息等。常见的预定义常量有:

  • PHP_VERSION:当前 PHP 解析器的版本
  • PHP_OS:服务器的操作系统
  • PHP_INT_MAX:最大的整数值
  • E_ERROR、E_WARNING、E_PARSE等:错误报告级别

常量数组

在 PHP 7 及以上版本中,常量也可以是数组,定义方法和普通常量是一样的,名称+值,值为数组即可

<?php
 define("lawking",["Hacker_lawking","fawang","ctf"]);
 const site_url = ["https://lawking.top","https://fawang.top"];
 echo site_url[1];
 echo lawking[1];
?>

或者写的规范一些:

<?php
 define("lawking",[
     "Hacker_lawking",
     "fawang",
     "ctf"
 ]);
 const site_url = [
     "https://lawking.top",
     "https://fawang.top"
 ];
 echo site_url[1];
 echo lawking[1];
?>

魔法常量(补充)

PHP魔法常量(Magic Constants)是PHP提供的一组特殊常量,它们的值会根据在代码中的位置和上下文自动变化。这些常量以双下划线 __ 开头和结尾,因此也被称为”双下划线常量”

一些主要的魔法常量:

  • __Line__ 当前行号
  • __FILE__ 当前文件的完整路径和文件名
  • __DIR__ 当前文件所在的目录
  • __FUNCTION__ 当前函数名
  • __CLASS__ 当前类名
  • __TRAIT__ 当前 Trait 名(PHP 5.4+)
  • __METHOD__ 当前方法名(包含类名)
  • __NAMESPACE__ 当前命名空间名(PHP 5.3+)

魔法常量主要用于调试和日志记录,自动加载和路径管理,错误处理,配置和环境管理等,特点是只读,不能被重新赋值,且值根据代码位置自动变化,在脚本编译时就确定了值,在任何作用域都可以使用,还必须注意大小写,因为对大小写也敏感

PHP字符串变量

字符串变量用于存储并处理文本,在创建字符串后,我们就可以对其进行操作,可以在函数中使用,或者直接把它存储到变量中

PHP 并置运算符

在 PHP 中,只有一个字符串运算符,并置运算符 . 用于把两个字符串连接起来,之前讲过,不过效果没有直接使用 , 号快

<?php
 $txt1="Hello world!";
 $txt2="What a nice day!";
 echo $txt1 , " " , $txt2;
 echo $txt1 . " " . $txt2;
?>

如上,就是将两个字符串拼接并在中间添加空格,使用逗号也是可以的

PHP strlen()函数

字面意思,str 字符串,len 长度,strlen 用来获取字符串长度的函数,常常用在循环和其他函数中,因为那时确定字符串何时结束是很重要的

PHP strpos()函数

strpos() 函数用于查找字符串中的一个字符或者一段指定的文本,会返回第一个字符的位置,索引开始是0,所以是从 0,1,2,3,4…算起,如下:

<?php
 echo strpos("hello world!","world");
?>

前面是被查找的字符串,后面是查找的内容,上面这段代码会返回6,因为 w 是第6个,第一位 h 的索引是0

PHP String

完整的PHP String参考手册

PHP 运算符

在 PHP 中,使用 = 赋值运算符给变量赋值,使用 + 算术运算符把值加在一起

PHP 算术运算符

+ - * / 加减乘除,这些就不用说了,基本上都理解,% 用于取模(就是取余数),-x 设置相反数,2就是-2,-1就是1,~x 按位取反,也叫位反转运算符,他会对操作数的每一位进行反正,0变1,1变0,比如5的二进制位0000 0100,按位取反就是1111 1011,也就是 -6 ,a.b并置运算符,前面有说,连接两个字符串用的

PHP 7+ 版本新增整除运算符 intdiv(),采用向下取整,a/b 的整数结果 ,如果不足就向下取整,比如10除6,为1.66666…,向下取整为1

PHP 赋值运算符

基本的赋值运算符为 =,意思是左操作数等于右侧表达式的值,x = y 表示左侧操作数为右侧的值,x += y 表示 x = x + y-=*= 分别表示 x = x - y x = x * y%=.= 分别表示 x = x % y a = a.b,具体含义可以看上面的算术运算符

PHP 递增/递减运算符

一共有四个,分别是 ++xx++--xx--,分别是预递增, x加1,然后返回 x ;后递增,先返回 x,然后x加1;预递减,x减1,然后返回x;后递减,返回x,然后x减1

PHP 比较运算符

== 等于 , === 绝对等于,!= 不等于,<>= 不等于, !== 不绝对等于,> 大于,< 小于,>= 大于等于,<= 小于等于,返回 false 和 true

PHP 逻辑运算符

and 与运算,or 或运算,xor 异或运算,&& 与,|| 或 ,! 非,返回布尔值

PHP 数组运算符

+ 集合,== 相等,===恒等,!= 不相等,<> 不相等,!== 不恒等,返回布尔值

三元运算符

语法:

condition ? value_if_true : value_if_false

condition 为真时返回 value_if_true,为假时返回 value_if_false,自PHP 5.3 起,可以省略中间部分,直接使用:

value_if_true ?: value_if_false

value_if_true 为真时返回 value_if_true,为假时返回 value_if_false

组合比较符

PHP 7+ 支持组合比较符 <=>,也叫太空船操作符,组合比较运算符可以轻松实现两个变量的比较,当然不仅限于数值类数据的比较

$c = $a <=> $b;

如果变量a大于变量b,变量c的值为1,小于则变量c的值为-1,等于则为0

PHP If..Else语句

因为有 python 的基础,并且时间关系,就不讲太细节了,主要是 if 、if…else、if…elseif…else、switch,switch语句是在若干条件之一成立时执行一个代码块

if…elseif..else 和 switch 的区别时,switch 是有选择的执行若干代码块之一,语法如下

<?php
switch (expression) {
 case value1:
     // 代码块1
     break;
 case value2:
     // 代码块2
     break;
 // 更多的 case 语句
 default:
     // 如果没有匹配的值
}
?>

expression 是要被比较的表达式,case value 是可能的值,如果被比较的表达式值等于某个 case 的值,就执行相应代码块,break 用于终止代码跳转到下一个 case,default 是可选的,用于指定没有匹配的值时执行的代码块

PHP 数组

数组时一个能在单个变量中存储多个值的特殊变量,通过键访问其中的值

创建数组

array()函数用于创建数组,数组分为三种,分别是数值数组,关联数组,这两个一个是 数字id 的键,一个是 指定的键,还有一种就是多维数组,包含一个或多个数组的数组,数字数组可以自动分配id,从0开始,也可以手动分配id

获取数组的长度

count()函数用于返回数组的长度(元素的数量)

遍历数值数组

遍历并打印数值数组中的所有值,您可以使用 for 循环

<?php
$cars=array("Volvo","BMW","Toyota");
$arrlength=count($cars);

for($x=0;$x<$arrlength;$x++)
{
 echo $cars[$x];
 echo "<br>";
}
?>

条件是x为0,然后x小于3,然后x+1,执行打印操作,然后换行,下一次循环继续比较,直到x为3后不小于变量arrlength,停止循环

关联数组

关联数组是使用您分配给数组的指定的键的数组,有两种创建方法,如下:

$age=array("Peter"=>"35","Ben"=>"37","Joe"=>"43");

$age['Peter']="35";
$age['Ben']="37";
$age['Joe']="43";

随后可以在脚本中使用指定的键(Peter,Ben,Joe)

遍历关键数组

遍历并打印关联数组中的所有值,您可以使用 foreach 循环,如下:

<?php
$age=array("Peter"=>"35","Ben"=>"37","Joe"=>"43");

foreach($age as $x=>$x_value)
{
 echo "Key=" . $x . ", Value=" . $x_value;
 echo "<br>";
}
?>

先是创建了一个 age 数组,然后遍历 数组 age 到变量x和变量x_value,分别接受键和值,每次遍历一组,然后输出,换行,然后继续遍历下一组,直到数组全部遍历完毕

PHP 数组排序

数组中的元素可以按字母或数字顺序进行降序或升序排列

  • sort() 对数组进行升序排列
  • rsort() 对数组进行降序排列
  • asort() 根据关联数组的值,对数组进行升序排列
  • ksort() 根据关联数组的键,对数组进行升序排列
  • arsort() 根据关联数组的值,对数组进行降序排列
  • krsort() 根据关联数组的键,对数组进行降序排列
<?php
// 按字母升序排列
$cars=array("Volvo","BMW","Toyota");
sort($cars);
?>

用法如上,直接在函数的参数里面传入数组变量即可

PHP 超级全局变量

PHP中预定义了几个超级全局变量(superglobals) ,这意味着它们在一个脚本的全部作用域中都可用。 你不需要特别说明,就可以在函数及类中使用

PHP 超级全局变量列表:

  • $GLOBALS
  • $_SERVER
  • $_REQUEST
  • $_POST
  • $_GET
  • $_FILES
  • $_ENV
  • $_COOKIE
  • $_SESSION

PHP $GLOBALS

$GLOBALS 是PHP的一个超级全局变量组,在一个PHP脚本的全部作用域中都可以访问,$GLOBALS 是一个包含了全部变量的全局组合数组,变量的名字就是数组的键

这个前面在将全局变量的时候有用到过

PHP $_SERVER

$_SERVER 是一个包含了诸如头信息(header)、路径(path)、以及脚本位置(script locations)等等信息的数组。这个数组中的项目由 Web 服务器创建。不能保证每个服务器都提供全部项目;服务器可能会忽略一些,或者提供一些没有在这里列举出来的项目

以下实例中展示了如何使用$_SERVER中的元素:

<?php 
echo $_SERVER['PHP_SELF'];
echo "<br>";
echo $_SERVER['SERVER_NAME'];
echo "<br>";
echo $_SERVER['HTTP_HOST'];
echo "<br>";
echo $_SERVER['HTTP_REFERER'];
echo "<br>";
echo $_SERVER['HTTP_USER_AGENT'];
echo "<br>";
echo $_SERVER['SCRIPT_NAME'];
?>

具体描述

PHP $_REQUEST

PHP $_REQUEST 用于收集HTML表单提交的数据

以下实例显示了一个输入字段(input)及提交按钮(submit)的表单(form)。 当用户通过点击 “Submit” 按钮提交表单数据时, 表单数据将发送至<form>标签中 action 属性中指定的脚本文件。 在这个实例中,我们指定文件来处理表单数据。如果你希望其他的PHP文件来处理该数据,你可以修改该指定的脚本文件名。 然后,我们可以使用超级全局变量 $_REQUEST 来收集表单中的 input 字段数据:

<html>
<body>

<form method="post" action="<?php echo $_SERVER['PHP_SELF'];?>">
Name: <input type="text" name="fname">
<input type="submit">
</form>

<?php 
$name = $_REQUEST['fname']; 
echo $name; 
?>

</body>
</html>

上面这段代码的运行结果就是根据用户输入的内容显示用户输入的用户名,不过这样写会存在XSS漏洞,因为直接将用户输入的内容输出,没有过滤,浏览器就会直接将用户输入的js代码解析并执行

PHP $_POST

PHP $_POST 被广泛应用于收集表单数据,在HTML form标签的指定该属性:”method=”post”。

以下实例显示了一个输入字段(input)及提交按钮(submit)的表单(form)。 当用户通过点击 “Submit” 按钮提交表单数据时, 表单数据将发送至<form>标签中 action 属性中指定的脚本文件。 在这个实例中,我们指定文件来处理表单数据。如果你希望其他的PHP文件来处理该数据,你可以修改该指定的脚本文件名。 然后,我们可以使用超级全局变量 $_POST 来收集表单中的 input 字段数据:

<html>
<body>

<form method="post" action="<?php echo $_SERVER['PHP_SELF'];?>">
Name: <input type="text" name="fname">
<input type="submit">
</form>

<?php 
$name = $_POST['fname']; 
echo $name; 
?>

</body>
</html>

PHP $_GET

PHP $_GET 同样被广泛应用于收集表单数据,在HTML form标签的指定该属性:”method=”get”

$_GET 也可以收集URL中发送的数据

这里还是上面的代码,简单修改后使用,就会存在反射性XSS:

http://154.37.221.123/index.php?fname=<script>alert("哈喽,我是法王")</script>

PHP While循环

当我们需要需要让相同的代码块一次又一次地重复运行时,就需要用到循环语句,while循环是只要条件成立,则循环执行代码块,do…while首先执行一次代码块,然后在指定的条件成立时重复这个循环,for 循环会执行代码块指定的次数,foreach根据数组中每个元素来循环代码块

while 循环

基础语法:

while (条件)
{
 要执行的代码;
}

do…while 循环

基础语法:

do
{
 要执行的代码;
}
while (条件);

do…while 语句会至少执行一次代码,然后检查条件,只要条件成立,就会重复进行循环

PHP For循环

for 循环用于您预先知道脚本需要运行的次数的情况

基础语法:

for (初始值; 条件; 增量)
{
 要执行的代码;
}

初始值,主要是初始化一个变量,用来当作计数器,条件,循环执行的限制条件,增量,主要用于递增计数器

上面的初始值和增量参数可为空,或者有多个表达式(用逗号分隔)

foreach循环

foreach 循环用于遍历数组

基础语法:

foreach ($array as $value)
{
 要执行代码;
}

每进行一次循环,当前数组元素的值就会被赋值给 $value 变量(数组指针会逐一地移动),在进行下一次循环时,您将看到数组中的下一个值

foreach ($array as $key => $value)
{
 要执行代码;
}

每一次循环,当前数组元素的键与值就都会被赋值给 $key 和 $value 变量(数字指针会逐一地移动),在进行下一次循环时,你将看到数组中的下一个键与值

PHP魔术常量

这一点在前面的PHP常量中有说明

PHP命名空间

PHP 命名空间(namespace)是在 PHP 5.3 中加入的,目的是解决重名问题,PHP中不允许两个函数或者类出现相同的名字,否则会产生一个致命的错误,所以就需要使用到命名空间,它提供了一个逻辑容器,将代码组织在不同的”空间”中

主要解决命名冲突,同时提供清晰的代码组织方式,并且提高代码的可维护性

定义命名空间

定义命名空间的基本语法:

<?php
 // 基本语法:namespace + 命名空间名称
 namespace MyNamespace;
 // 在命名空间中定义类
 class MyClass {
     public function sayHello() {
         echo "Hello from MyNamespace!";
     }
}

可以在同一个文件中定义不同的命名空间代码,不过为了更易读,建议使用大括号在一个文件中如果要定义多个命名空间:

<?php
 namespace MyProject {
     const CONNECT_OK = 1;
     class Connection { /* ... */ }
     function connect() { /* ... */  }
 }

 namespace AnotherProject {
     const CONNECT_OK = 1;
     class Connection { /* ... */ }
     function connect() { /* ... */  }
 }
?>

这样就能够很清晰的区分出这是两个命名空间

当你要在一个文件中同时包含全局代码和命名空间代码时,就需要使用大括号语法,全局命名空间不要带名称

<?php
// 使用大括号语法混合全局代码和命名空间代码
namespace MyApp {
 class MyClass {
     public function sayHello() {
         echo "Hello from namespace!";
     }
 }
}
namespace {  // 全局命名空间(不带名称)
 function globalFunction() {
     echo "This is a global function";
 }

 echo "全局代码执行\n";

 // 使用命名空间中的类
 $obj = new \MyApp\MyClass();
 $obj->sayHello();
}
?>

在声明命名空间之前唯一合法的代码是用于定义源文件编码方式的 declare 语句。所有非 PHP 代码包括空白符都不能出现在命名空间的声明之前

<?php
 declare(encoding='UTF-8'); //定义多个命名空间和不包含在命名空间中的代码
 namespace MyProject {

 const CONNECT_OK = 1;
 class Connection { /* ... */ }
 function connect() { /* ... */  }
 }

 namespace { // 全局代码
 session_start();
 $a = MyProject\connect();
 echo MyProject\Connection::start();
 }
?>

子命名空间

与目录和文件的关系很像,PHP 命名空间也允许指定层次化的命名空间的名称。因此,命名空间的名字可以使用分层次的方式定义

<?php
 namespace MyProject\Sub\Level;  //声明分层次的单个命名空间

 const CONNECT_OK = 1;
 class Connection { /* ... */ }
 function Connect() { /* ... */  }
?>

上面的例子创建了常量 MyProject\Sub\Level\CONNECT_OK,类 MyProject\Sub\Level\Connection 和函数 MyProject\Sub\Level\Connect

命名空间使用

PHP 命名空间中的类名可以通过三种方式引用

非限定名称

定义:不包含反斜杠前缀的名称,就是最简单的类名

示例:

<?php
 namespace MyApp;
 // 在命名空间中定义类
 class MyClass {
     public static function sayHello() {
         echo "Hello from MyApp\MyClass\n";
     }
 }
 // 在同一个命名空间中使用非限定名称
 $obj = new MyClass();  // 解析为 MyApp\MyClass
 MyClass::sayHello();   // 解析为 MyApp\MyClass::sayHello()
?>

作用:简化代码书写,减少重复,避免每次都写完整的命名空间路径

限定名称

定义:包含前缀但不可以反斜杠开头打打名称

示例:

<?php
 // 全局代码
 namespace MyApp\Services {
     class EmailService {
         public function send() {
             echo "Email sent\n";
         }
     }
 }
 namespace {
     // 在全局命名空间中使用限定名称
     $emailService = new MyApp\Services\EmailService();  // 解析为 \MyApp\Services\EmailService
     $emailService->send();
}
?>

作用:引用同级或子级命名空间中的类,组织相关的类在逻辑分组中

完全限定名称

定义:以反斜杠开头的名称,明确指定完整的命名空间路径

<?php
 namespace MyApp\Models {
     class User {
         public function getName() {
             return "User from Models namespace";
         }
     }
 }
 namespace MyApp\Controllers {
     class UserController {
         public function getUser() {
             // 完全限定名称 - 明确指定完整的命名空间路径
             $user = new \MyApp\Models\User();  // 不管当前在哪个命名空间,都解析为 \MyApp\Models\User

             // 对比限定名称
             // $user = new Models\User();  // 解析为 \MyApp\Controllers\Models\User (可能不存在)

             return $user->getName();
         }
     }
 }
 namespace {
     // 全局命名空间中使用完全限定名称
     $controller = new \MyApp\Controllers\UserController();
     echo $controller->getUser();

     // 访问全局类的完全限定名称
     $exception = new \Exception("This is a global Exception");
}
?>

作用:明确指定类的完整路径,不受当前命名空间的影响,跨多个命名空间引用类,引用全局类

小总结

这三个我也看得很迷糊,不过大抵是三种不同的引用方法,我问了ai之后,自己理解成相对路径和绝对路径以及不适用路径的情况,非限定名称就是不适用路径,从当前空间导入;限定名称就是相对路径,相对于当前的命名空间;完全限定名称就是绝对路径,从根号开始的完整路径

这样设计就是跟我们在文件系统中使用相对路径和绝对路径一样,为不同场景提供最合适的引用方式

接下来是几个示例,可以看看:

非限定名称就像使用当前目录下的文件,代码如下:

<?php
 // 文件: index.php
 namespace lib;  // 当前命名空间是lib
 // 非限定名称 - 直接使用类名,PHP会在当前命名空间中查找
 $db = new Database();  // 等同于 new \lib\Database()
 $user = new User();    // 等同于 new \lib\User()
?>

限定名称就像使用相对路径,就像下面这段代码:

<?php
 // 文件: app.php
 namespace app;
 // 限定名称 - 相对于当前命名空间的路径
 $db = new lib\Database();  // 从当前app命名空间找lib\Database
                            // 实际寻找 \app\lib\Database()
?>

这两个都是在这个当前文件中使用,而接下来这个完全限定名称就像绝对路径,代码如下:

<?php
 // 文件: main.php
 namespace app;
 // 完全限定名称 - 从根命名空间开始的完整路径
 $db = new \lib\Database();  // 无论在哪个命名空间,都找\lib\Database
 $user = new \lib\User();    // 无论在哪个命名空间,都找\lib\User
?>

导入之后也能够在其他文件使用,代码如下:

// 文件1:Models/User.php
<?php
 namespace Models;
 class User {
     public function save() {
         echo "保存用户到数据库\n";
     }
 }
?>

// 文件2:Controllers/UserController.php
<?php
 namespace Controllers;
 // 引入其他命名空间的类
 use Models\User;  // 导入Models\User类
 class UserController {
     public function create() {
         // 非限定名称 - 在当前命名空间或通过use导入查找
         $user = new User();  // 因为用了use,所以找到Models\User
         $user->save();

         // 完全限定名称 - 绝对路径
         $user2 = new \Models\User();
         $user2->save();
     }

     public function update() {
         // 限定名称 - 相对路径(从当前命名空间开始)
         $user = new Models\User();  // 从\Controllers找\Models\User
                                    // 实际找的是\Controllers\Models\User(如果存在的话)
     }
 }
?>

// 文件3:index.php
<?php
 require 'Models/User.php';
 require 'Controllers/UserController.php';
 // 在全局命名空间
 $controller = new Controllers\UserController();
 $controller->create();
 // 直接使用完全限定名称
 $user = new \Models\User();
 $user->save();
?>

非限定名称:Database() → 在当前命名空间找 Database

限定名称: Models\Database() → 从当前命名空间查找 Models\Database()

完全限定名称:\Models\Database() → 从根路命名空间查找 Models\Database()

命名空间和动态语言特征

PHP 命名空间的实现受到其语言自身的动态特征的影响。因此,如果要将下面的代码转换到命名空间中,动态访问元素

<?php
  class classname
  {
      function __construct()
      {
          echo __METHOD__,"\n";
      }
  }
  function funcname()
  {
      echo __FUNCTION__,"\n";
  }
  const constname = "global";

  $a = 'classname';
  $obj = new $a; // prints classname::__construct
  $b = 'funcname';
  $b(); // prints funcname
  echo constant('constname'), "\n"; // prints global
?>

必须使用完全限定名称(包括命名空间前缀的类名称)。注意因为在动态的类名称、函数名称或常量名称中,限定名称和完全限定名称没有区别,因此其前导的反斜杠是不必要的

动态访问命名空间的元素

<?php
  namespace namespacename;
  class classname
  {
      function __construct()
      {
          echo __METHOD__,"\n";
      }
  }
  function funcname()
  {
      echo __FUNCTION__,"\n";
  }
  const constname = "namespaced";

  include 'example1.php';

  $a = 'classname';
  $obj = new $a; // 输出 classname::__construct
  $b = 'funcname';
  $b(); // 输出函数名
  echo constant('constname'), "\n"; // 输出 global

  /* 如果使用双引号,使用方法为 "\\namespacename\\classname"*/
  $a = '\namespacename\classname';
  $obj = new $a; // 输出 namespacename\classname::__construct
  $a = 'namespacename\classname';
  $obj = new $a; // 输出 namespacename\classname::__construct
  $b = 'namespacename\funcname';
  $b(); // 输出 namespacename\funcname
  $b = '\namespacename\funcname';
  $b(); // 输出 namespacename\funcname
  echo constant('\namespacename\constname'), "\n"; // 输出 namespaced
  echo constant('namespacename\constname'), "\n"; // 输出 namespaced
?>

这些是复制菜鸟编程的,我也没太看懂,可能是代码一多久迷糊了,我让ai给我讲解的这个板块——命名空间和动态语言特征

PHP作为动态语言有几个重要特征:

变量名可以动态确认:

<?php
 $varName = 'username';
 $$varName = '张三';  // 相当于 $username = '张三';
 echo $username;     // 输出: 张三
?>

类名和方法名可以动态调用:

<?php
 class UserService {
     public function getUser() {
         return '用户信息';
     }
 }
 $className = 'UserService';
 $methodName = 'getUser';
 // 动态创建类实例
 $obj = new $className();  // new UserService()
 // 动态调用方法
 $result = $obj->$methodName();  // $obj->getUser()
 echo $result;  // 输出: 用户信息
?>

就是可以使用变量来指定类名、方法名和变量名

命名空间与动态特性的结合

动态命名空间类的实例化:

<?php
 namespace App\Services;
 class EmailService {
     public function send() {
         echo "发送邮件\n";
     }
 }
 // 动态创建带命名空间的类
 $fullClassName = 'App\Services\EmailService';
 $obj = new $fullClassName();  // 必须使用完全限定名称
 $obj->send();
 // 或者这样
 $namespace = 'App\Services';
 $className = 'EmailService';
 $fullClassName = $namespace . '\\' . $className;
 $obj = new $fullClassName();
 $obj->send();
?>

必须使用完全限定名称(包括命名空间前缀的类名称)。注意因为在动态的类名称、函数名称或常量名称中,限定名称和完全限定名称没有区别,因此其前导的反斜杠是不必要的

动态调用命名空间中的类方法:

<?php
 namespace Database\Adapters;
 class MySQL {
     public function connect() {
         return "连接MySQL数据库";
     }

     public function query($sql) {
         return "执行SQL: $sql";
     }
 }
 // 动态使用
 $driver = 'MySQL';
 $fullClass = 'Database\Adapters\\' . $driver;
 $obj = new $fullClass();
 echo $obj->connect();  // 输出: 连接MySQL数据库
 $method = 'query';
 $sql = "SELECT * FROM users";
 echo $obj->$method($sql);  // 动态调用方法
?>

namespace关键字和NAMESPACE常量

PHP支持两种抽象的访问当前命名空间内部元素的方法,__NAMESPACE__ 魔术常量和namespace关键字

常量__NAMESPACE__的值是包含当前命名空间名称的字符串。在全局的,不包括在任何命名空间中的代码,它包含一个空的字符串

<?php
 namespace App\Controllers\Admin;
 class UserController {
     public function test() {
         // 输出当前命名空间名称
         echo __NAMESPACE__;  // 输出: App\Controllers\Admin
     }
 }
 echo __NAMESPACE__;  // 如果在命名空间外,输出空字符串
?>

常量 __NAMESPACE__ 在动态创建名称时很有用,关键字 namespace 可用来显式访问当前命名空间或子命名空间中的元素。它等价于类中的 self 操作符

<?php
 namespace MyProject;

 use blah\blah as mine; // 引入了 blah\blah 命名空间,并定义了个别名mine

 mine\mine(); // 调用函数 blah\blah\mine()
 namespace\blah\mine(); // 调用函数 MyProject\blah\mine()

 namespace\func(); // 调用函数 MyProject\func()
 namespace\sub\func(); // 调用函数 MyProject\sub\func()
 namespace\cname::method(); // 调用 MyProject\cname 类的静态方法
 $a = new namespace\sub\cname(); // 实例化 MyProject\sub\cname 类的对象
 $b = namespace\CONSTANT; // 将常量 MyProject\CONSTANT 的值赋给 $b
?>

namespace操作符, 全局代码:

<?php
 namespace\func(); // calls function func()
 namespace\sub\func(); // calls function sub\func()
 namespace\cname::method(); // calls static method "method" of class cname
 $a = new namespace\sub\cname(); // instantiates object of class sub\cname
 $b = namespace\CONSTANT; // assigns value of constant CONSTANT to $b
?>

类似于当前目录

使用命名空间:别名/导入

前面代码里面有,使用 use 可以使用别的命名空间,设置别名使用 as

使用use操作符导入/使用别名:

<?php
 namespace foo;
 use My\Full\Classname as Another;

 // 下面的例子与 use My\Full\NSname as NSname 相同
 use My\Full\NSname;

 // 导入一个全局类
 use \ArrayObject;

 $obj = new namespace\Another; // 实例化 foo\Another 对象
 $obj = new Another; // 实例化 My\Full\Classname 对象
 NSname\subns\func(); // 调用函数 My\Full\NSname\subns\func
 $a = new ArrayObject(array(1)); // 实例化 ArrayObject 对象
 // 如果不使用 "use \ArrayObject" ,则实例化一个 foo\ArrayObject 对象
?>

一行中包含多个use语句:

<?php
 use My\Full\Classname as Another, My\Full\NSname;

 $obj = new Another; // 实例化 My\Full\Classname 对象
 NSname\subns\func(); // 调用函数 My\Full\NSname\subns\func
?>

导入操作是在编译执行的,但动态的类名称、函数名称或常量名称则不是,所以导入别名就不能使用

<?php
 namespace App\Controllers;
 use App\Models\User;  // 编译时导入
 use App\Services as Svc;  // 编译时导入并设置别名
 class UserController {
     public function show() {
         // ✅ 编译时确定,可以使用导入的别名
         $user = new User();  // 指向 App\Models\User

         // ✅ 编译时确定,可以使用导入的别名  
         $emailService = new Svc\Email();  // 指向 App\Services\Email
     }

     public function dynamicCreate($modelName) {
         // ❌ 这是动态类名,在运行时确定
         // 导入别名在这里不起作用!
         $className = 'User';  // 这会查找 \App\Controllers\User(当前命名空间)
         $obj = new $className();  // 可能报错:类不存在

         // ✅ 正确的做法:必须使用完整类名
         $fullClassName = 'App\Models\User';
         $obj2 = new $fullClassName();  // 正确

         // ✅ 或者使用__NAMESPACE__常量
         $dynamicClass = __NAMESPACE__ . '\\' . $modelName;
         $obj3 = new $dynamicClass();
     }
 }
?>

所以这就是为什么动态类名、函数名、常量比须写完整路径,不能享受导入别名的便利

想象编译时就像预先整理好书架:

  • use语句就像是把书按类别放到指定位置
  • 编译器在编译时就知道每本书在哪里

运行时就像读书的时候:

  • 直接书名调用(非限定名称)→ 很快找到
  • 动态书名调用 → 还得重新找完整路径,导入规则不管用

导入和动态名称:

<?php
    use My\Full\Classname as Another, My\Full\NSname;

    $obj = new Another; // 实例化一个 My\Full\Classname 对象
    $a = 'Another';
    $obj = new $a;      // 实际化一个 Another 对象
?>

另外,导入操作只影响非限定名称和限定名称。完全限定名称由于是确定的,故不受导入的影响

导入和完全限定名称:

<?php
    use My\Full\Classname as Another, My\Full\NSname;

    $obj = new Another; // 实例化 My\Full\Classname 类
    $obj = new \Another; // 实例化 Another 类
    $obj = new Another\thing; // 实例化 My\Full\Classname\thing 类
    $obj = new \Another\thing; // 实例化 Another\thing 类
?>

后备全局函数/常量

“后备” 就是”备用方案”的意思。当PHP在当前命名空间找不到某个函数或常量时,它会去全局命名空间(根命名空间)查找作为后备选择

类名没有后备,找不到就直接报错,所以必须是完全限定名称,只有函数和常量才有后备选择,如下:

<?php
 namespace App\Controllers;
 // 假设我们自定义了一个函数
 function strlen($str) {
     return "自定义的strlen函数: " . \strlen($str);  // 调用全局的strlen
 }
 class TestController {
     public function test() {
         // 非限定的函数名 → 解析策略:
         // 1. 先查找当前命名空间的strlen函数 ✅ 找到了自定义的
         // 2. 如果找不到,后备到全局命名空间的strlen函数

         echo strlen("hello");  // 输出: 自定义的strlen函数: 5

         // 调用全局的strlen需要完全限定
         echo \strlen("hello");  // 输出: 5
     }
 }
?>

常量后备示例,如下:

<?php
 namespace App\Config;
 // 定义命名空间内的常量
 const DATABASE = 'mysql_db';
 class Config {
     public function getDatabase() {
         // 非限定常量名 → 解析策略:
         // 1. 先查找当前命名空间的DATABASE常量 ✅ 找到了
         echo DATABASE;  // 输出: mysql_db

         // 如果当前命名空间没有定义:
         // echo VERSION;  // 会后备查找全局命名空间的VERSION常量
         // 如果全局也没有就会报错
     }
 }
?>

全局空间

如果没有定义任何命名空间,所有的类与函数的定义都是在全局空间,与 PHP 引入命名空间概念前一样。在名称前加上前缀 \ 表示该名称是全局空间中的名称,即使该名称位于其它的命名空间中时也是如此

在没有命名空间时,所有代码都在全局空间,直接使用即可,如果有命名空间,要访问全局命名空间的函数或常量或类,就需要使用 \ 前缀

\ 前缀的作用就是强制指定使用全局命名空间,无论当前在哪个命名空间

命名空间的顺序

自从有了命名空间之后,最容易出错的该是使用类的时候,这个类的寻找路径是什么样的了

名称解析遵循下列规则:

  • 对完全限定名称的函数,类和常量的调用在编译时解析
  • 所有的非限定名称和限定名称(非完全限定名称)根据当前的导入规则在编译时进行转换。例如,如果命名空间 A\B\C 被导入为 C,那么对 C\D\e() 的调用就会被转换为 A\B\C\D\e()
  • 在命名空间内部,所有的没有根据导入规则转换的限定名称均会在其前面加上当前的命名空间名称。例如,在命名空间 A\B 内部调用 C\D\e(),则 C\D\e() 会被转换为 A\B\C\D\e()
  • 非限定类名根据当前的导入规则在编译时转换(用全名代替短的导入名称)。例如,如果命名空间 A\B\C 导入为C,则 new C() 被转换为 new A\B\C()
  • 在命名空间内部(例如A\B),对非限定名称的函数调用是在运行时解析的。例如对函数 foo() 的调用是这样解析的:在当前命名空间查找名为 A\B\foo() 函数,尝试查找并调用全局空间中的 foo() 函数
  • 在命名空间(例如A\B)内部对非限定名称或限定名称类(非完全限定名称)的调用是在运行时解析的。下面是调用 new C()new D\E() 的解析过程: new C()的解析:
    1. 在当前命名空间中查找A\B\C类。
    2. 尝试自动装载类A\B\C
    new D\E()的解析:
    1. 在类名称前面加上当前命名空间名称变成:A\B\D\E,然后查找该类。
    2. 尝试自动装载类 A\B\D\E
    为了引用全局命名空间中的全局类,必须使用完全限定名称 new \C()

结尾

这段我真的看迷糊了,不过慢慢看还是能懂的,就是非常绕,无法一眼看出来,然后就是 PHP 的一些函数了,肯定是没办法学习全部,慢慢看,或者看一下比较典型的,不过我让ai生成了一下,发现也是非常多,所以我打算单独再写一篇文章来学习PHP的一些危险的函数,这篇文章就到这了

  • 核心函数:约200-300个
  • 字符串函数:约100个
  • 数组函数:约80个
  • 文件系统函数:约50个
  • 日期时间函数:约30个
  • 数学函数:约50个
  • 网络函数:约40个
  • 错误处理函数:约20个
  • 其他模块函数:数百个
文末附加内容
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇