当前版本是8.1
类型
- boolean
- int
- float
- string
- 数字字符串
- array
- iterable 迭代对象
- object
- Enum
- Resource
- NULL
- CallBack
数字字符串
PHP string 可以被解释为 int 或 float 类型,则它被视为数字字符串。
当一个string需要当作一个数字进行运算时:
- string数字再int范围则是int,否则是float
- 如果再允许,前导数字是int且string符合int范围,则是int,否则是float。此外会导致
E_WARNING
- string不是数字,抛出
TypeError
Iterable可迭代对象
Iterable是 PHP 7.1 中引入的一个伪类型。它接受任何 array 或实现了 Traversable 接口的对象。这些类型都能用 foreach 迭代, 也可以和 生成器 里的 yield from 一起使用。
<?php
function bar(): iterable {
return [1, 2, 3];
}
?>
生成器:
<?php
function gen(): iterable {
yield 1;
yield 2;
yield 3;
}
foreach(gen() as $each_one) {
}
?>
object
new
运算符自动返回一个引用,所以对象传递其实引用(传递指针),并不值复制。
$a = new A();
change($a)
function change(A $tmp) {
// 会$a覆盖初始化值
$tmp->a = 1;
}
-
如果对象转成对象,它不会有任何变化
-
其他类型转化成对象,将会创建一个内置类stdClass 的实例
-
array转成object,键名会变成属性
Enum枚举
类似于常量
。枚举是在类、类常量基础上的约束层, 目标是提供一种能力:定义包含可能值的封闭集合类型。
<?php
enum Suit
{
case Hearts;
case Diamonds;
case Clubs;
case Spades;
}
function do_stuff(Suit $s)
{
// ...
}
do_stuff(Suit::Spades);
?>
resource
资源 resource 是一种特殊变量,保存了到外部资源的一个引用。资源是通过专门的函数来建立和使用的。
资源类型变量保存有为打开文件、数据库连接、图形画布区域等的特殊句柄。
callback
回调可以通过 callback 类型声明来表示。
call_user_func
,usort
,array_filter()
等函数可以接收用户自定义的回调函数作为参数。
php可以将函数以string方式传递,但是语言结构不行例如:array(),echo,empty(),eval(),exit(),isset(),list(),print 或 unset()。
一个已实例化的 object 的方法被作为 array 传递,
<?php
// 回调函数示范
function my_callback_function() {
echo 'hello world!';
}
// 回调方法示范
class MyClass {
static function myCallbackMethod() {
echo 'Hello World!';
}
}
// 类型 1:简单的回调
call_user_func('my_callback_function');
// 类型 2:静态类方法回调
call_user_func(array('MyClass', 'myCallbackMethod'));
// 类型 3:对象方法回调
$obj = new MyClass();
call_user_func(array($obj, 'myCallbackMethod'));
// 类型 4:静态类方法回调
call_user_func('MyClass::myCallbackMethod');
// 类型 5:父级静态类回调
class A {
public static function who() {
echo "A\n";
}
}
class B extends A {
public static function who() {
echo "B\n";
}
}
call_user_func(array('B', 'parent::who')); // A
// 类型 6:实现 __invoke 的对象用于回调
class C {
public function __invoke($name) {
echo 'Hello ', $name, "\n";
}
}
$c = new C();
call_user_func($c, 'PHP!');
?>
类型声明
类型声明可以用户函数的参数或者返回值
列举特殊的一些类型
类型 | 说明 |
---|---|
self | 声明相同类的实例,仅可在类中使用 |
parent | 声明父类的实例,仅可在类中使用 |
callback | 值必须时一个有效的callback |
iterable | 值必须为 array 或 instanceof Traversable。 |
mixed | 任何类型 |
-
返回值类型声明
function sum($a, $b): float { return $a + $b; } class C{} function sum($a, $b): C { return $a + $b; }
-
允许为空的Nullable类型,类型声明允许前置一个问号 (
?
) 用来声明这个值允许为指定类型,或者为null
。<?php class C {} function f(?C $c) { var_dump($c); } f(new C); f(null); ?>
预定义变量
-
$GLOBALS 引用全局作用域的全部变量
-
$_SERVER 服务器和执行环境信息
-
$_GET http get变量
-
$_POST http post变量
-
$_FILES http文件上传变量
-
$_REQUEST http request变量
-
$_SESSION session变量
-
$_ENV 环境变量
-
$_cookie http cookie变量
-
$php_errormsg 前一个错误信息
-
$http_response_header http响应头
<?php function get_contents() { file_get_contents("http://example.com"); var_dump($http_response_header); } get_contents(); var_dump($http_response_header); ?>
-
$argc 传递给脚本的参数数目
-
$argv 传递给脚本的参数数组
魔术常量
__LINE__
:文件当前行__FILE__
:当前文件路径和文件名__DIR__
:文件所在目录__FUNCTION__
:当前函数名,匿名函数则是{closure}
__CLASS__
:当前类,如果时trait则是调用trait的类名__TRAIT__
:trait名__METHED__
:类的方法名__NAMESPACE__
:当前命名空哦关键{ClassName}::class
:完整类名
特殊运算符
执行运算符
php支持执行运算符,使用反引号(``)
。php尝试将反引号内的内容作为shell命令执行
$commnad = "ipconfig";
echo $command;
//output
Ethernet adapter vEthernet (WSL):
Connection-specific DNS Suffix . :
Link-local IPv6 Address . . . . . : fe80::d457:3fa7:d496:d2ec%26
IPv4 Address. . . . . . . . . . . : 172.29.112.1
Subnet Mask . . . . . . . . . . . : 255.255.240.0
Default Gateway . . . . . . . . . :
数组运算符
$a+$b
:数组联合$a==$b
:数组具有相同键则为true$a===$b
:数组具有相同键,且顺序类型都相同则为true$a!=$b
:数组不相等为true$a<>$b
:数组不相等为true$a!===$b
:数组不全等
类型运算符
instanceof检查变量是否属于此类class
<?php
class MyClass
{
}
class NotMyClass
{
}
$a = new MyClass;
var_dump($a instanceof MyClass);
var_dump($a instanceof NotMyClass);
?>
特殊流程控制
流程控制的代替语法(不建议使用)
PHP 提供了一些流程控制的替代语法,包括 if
,while
,for
,foreach
和 switch
。替代语法的基本形式是把左花括号({)换成冒号(:),把右花括号(})分别换成 endif;
,endwhile;
,endfor;
,endforeach;
以及 endswitch;
。
<?php
if ($a == 5):
echo "a equals 5";
echo "...";
elseif ($a == 6):
echo "a equals 6";
echo "!!!";
else:
echo "a is neither 5 nor 6";
endif;
?>
foreach
提供遍历数组,对象等变量的简单方式
# 遍历迭代器
foreach (iterable_expression as $value)
statement
# 遍历迭代器,单元键复制给了key
foreach (iterable_expression as $key => $value)
statement
foreach不会修改current()
和next()
等函数所使用的数组内部指针
通过引用遍历更改数组的值
<?php
$a = [1,2,3,4,5];
$b = &$a;
foreach($b as &$each) {
$each++;
}
var_dump($a);
?>
foreach 和list组合遍历 多维数组
<?php
$array = [
[1, 2],
[3, 4],
];
foreach ($array as list($a, $b)) {
// $a 包含嵌套数组的第一个元素,
// $b 包含嵌套数组的第二个元素。
echo "A: $a; B: $b\n";
}
?>
match
match
表达式基于值的一致性进行分支计算。 match
表达式和 switch
语句类似, 都有一个表达式主体,可以和多个可选项进行比较。 与 switch
不同点是,它会像三元表达式一样求值。 与 switch
另一个不同点,它的比较是严格比较( ===
)而不是松散比较(==
)。 Match 表达式从 PHP 8.0.0 起可用。
<?php
$food = 'bar1';
$return_value = match ($food) {
'apple' => 'This food is an apple',
'bar,bar1' => 'This food is a bar',
'cake' => 'This food is a cake',
default => 'This food is a default food'
};
var_dump($return_value);
# output
// string(19) "This food is a cake"
?>
match
表达式跟 switch
语句相似,但是有以下关键区别:
match
比较分支值,使用了严格比较 (===
), 而 switch 语句使用了松散比较。match
表达式会返回一个值。match
的分支不会像switch
语句一样, 落空时执行下个 case。match
表达式必须彻底列举所有情况。如果没有一个case能对比,那么抛出Fatal error致命错误
declare
declare
结构用来设定一段代码的执行指令。declare
的语法和其它流程控制结构相似:
declare (directive)
statement
directive
部分允许设定 declare
代码段的行为。目前只认识三个指令:ticks
(更多信息见下面 ticks 指令)、 encoding
(更多信息见下面 encoding 指令)、 strict_types
指令。
因为本指令是在文件编译时处理的,所以指令只接受字面量的值。 无法使用变量和常量。下面为你演示:
<?php
// 这样是有效的:
declare(ticks=1);
// 这样是无效的:
const TICK_VALUE = 1;
declare(ticks=TICK_VALUE);
// 可以这样用:
declare(ticks=1) {
// 这里写完整的脚本
}
// 也可以这样用:
declare(ticks=1);
// 这里写完整的脚本
?>
tick指令
Tick(时钟周期,更像一个计数器)是一个在 declare
代码段中解释器每执行 N 条可计时的低级语句就会发生的事件。N 的值是在 declare
中的 directive
部分用 ticks=N
来指定的。
不是所有语句都可计时。通常条件表达式和参数表达式都不可计时。
<?php
declare(ticks=1);
// 每次 tick 事件都会调用该函数
function tick_handler()
{
echo "tick_handler() called\n";
}
register_tick_function('tick_handler'); // 引起 tick 事件,注册回调
$a = 1; // 引起 tick 事件
if ($a > 0) {
$a += 2; // 引起 tick 事件
print($a); // 引起 tick 事件
}
?>
encoding指令
可以用 encoding
指令来对每段脚本指定其编码方式。
<?php
declare(encoding='ISO-8859-1');
// 在这里写代码
?>
goto
goto
操作符可以用来跳转到程序中的另一位置。该目标位置可以用 区分大小写 的目标名称加上冒号来标记,而跳转指令是 goto
之后接上目标位置的标记。PHP 中的 goto
有一定限制,目标位置只能位于同一个文件和作用域,也就是说无法跳出一个函数或类方法,也无法跳入到另一个函数。也无法跳入到任何循环或者 switch 结构中。可以跳出循环或者 switch,通常的用法是用 goto
代替多层的 break
。
函数
自定义函数
任何有效的 PHP 代码都有可能出现在函数内部,甚至包括其它函数和 类 定义。
PHP 中的所有函数和类都具有全局作用域,可以定义在一个函数之内而在之外调用,反之亦然。
可变数量的参数列表
PHP 在用户自定义函数中支持可变数量的参数列表。由 ...
语法实现。
<?php
function sum(...$numbers) {
$acc = 0;
foreach ($numbers as $n) {
$acc += $n;
}
return $acc;
}
echo sum(1, 2, 3, 4);
?>
匿名函数
匿名函数(Anonymous functions),也叫闭包函数(closures
),允许 临时创建一个没有指定名称的函数。最经常用作回调函数 callable参数的值。当然,也有其它应用的情况。
匿名函数目前是通过 Closure 类来实现的。
闭包可以从父作用域中继承变量。 任何此类变量都应该用 use
语言结构传递进去。(php是显示的用use使用父作用域,而go是隐式)。
闭包的父作用域是定义该闭包的函数,而不一定是调用他的函数(这意味着要看闭包的use值,是去定义闭包函数的文件函数去查找)
<?php
$message = 'hello';
// 没有 "use"
$example = function () {
var_dump($message);
};
$example();
// 继承 $message
$example = function () use ($message) {
var_dump($message);
};
$example();
当在类的上下问中声明的匿名函数,$this是会自动绑定到匿名函数作用域中。(静态匿名函数除外 static closures)
箭头函数
是一种简洁的匿名函数,基本语法如下
fn (argument_list) => expr
他和go语法一样,使用的是隐式继承父作用域。他的功能和匿名函数相同
<?php
$y = 1;
// 箭头函数,隐式use $y
$fn1 = fn($x) => $x + $y;
// 匿名函数,显示use $y
$fn2 = function ($x) use ($y) {
return $x + $y;
};
var_export($fn1(3));
?>
first class callback syntax
todo
类
new 创建对象
- 使用
new
实例化类,当把一个对象已经创建的实例赋给一个新变量时,新变量会访问同一个实例,就和用该对象赋值一样。此行为和给函数传递入实例时一样。可以用 克隆 给一个已创建的对象建立一个新实例。
引用案例
<?php
# 对象引用案例
class SimpleClass {
}
$a = new SimpleClass();
$b = $a; // a,b指向同一个地址
$c = &$a; // 因为a已经是引用,现在在加一个&,相当于c保存了a的引用
$a->var = '$assigned will have this value'; // a指向的地址中var更改
$a = null; // a指向null,此时c还是存着a地址,b指向的是原来的地址
var_dump($a); // print null a指向null
var_dump($c); // print null c指向的a是null,所以是空
var_dump($b); // print class 所以b非空
?>
- 可以用 克隆 给一个已创建的对象建立一个新实例。
克隆案例
<?php
class SimpleClass {
function __clone() {
echo "调用clone时,如果类内定义clone魔术方法,则会调起";
}
}
$a = new SimpleClass();
$b = clone $a;
$a->var = 1;
$b->var = 2;
// print "调用clone时,如果类内定义clone魔术方法,则会调起";
var_dump($a); // print 1
var_dump($b); // print 2
?>
- 在类内部创建新对象,可以使用
new self
和new parent
。单例就是使用此特性
extends
个类可以在声明中用 extends
关键字继承另一个类的方法和属性。PHP 不支持多重继承,一个类只能继承一个基类。
- 如果父类不允许子类修改某方法则使用
final
关键字 - 当子类覆盖父类方法,父类强制参数可以改为可选参数;新参数为可选参数。移除参数或修改可选参数为必填参数则会fatal error
覆盖案例
<?php
class Base
{
public function foo(int $a) {
echo "Valid\n";
}
}
class Extend1 extends Base
{
function foo(int $a = 5)
{
parent::foo($a);
}
}
class Extend2 extends Base
{
function foo(int $a, $b = 5)
{
parent::foo($a);
}
}
nullsafe方法和属性
类属性和方法可以通过 “nullsafe” 操作符访问: ?->
。nullsafe 操作符和以上原来的属性、方法访问是一致的: 对象引用解析(dereference)为 null
时不抛出异常,而是返回 null
。 并且如果是链式调用中的一部分,剩余链条会直接跳过。
此操作的结果,类似于在每次访问前使用 is_null() 函数判断方法和属性是否存在,但更加简洁。
<?php
// 自 PHP 8.0.0 起可用
$result = $repository?->getUser(5)?->name;
?>
属性
-
访问控制
-
public
-
protected
-
private:私有属性,同一个类的对象即使不是同一个实例也可以互相访问对方的私有与受保护成员。 这是由于在这些对象的内部具体实现的细节都是已知的。
<?php class Test { private $foo; public function __construct($foo) { $this->foo = $foo; } private function bar() { echo 'Accessed the private method.'; } public function baz(Test $other) { // 我们可以改变私有属性: $other->foo = 'hello'; var_dump($other->foo); // 我们也可以调用私有方法: $other->bar(); } } $test = new Test('test'); $test->baz(new Test('other')); ?>
-
-
静态关键字 static
-
自读readonly,只能在初始化时定义属性,之后就无法修改
<?php
class Test {
public readonly string $prop;
public function __construct(string $prop) {
// Legal initialization.
$this->prop = $prop;
}
}
$test = new Test("foobar");
// Legal read.
var_dump($test->prop); // string(6) "foobar"
// Illegal reassignment. It does not matter that the assigned value is the same.
$test->prop = "foobar";
// Error: Cannot modify readonly property Test::$prop
?>
自动加载
spl_autoload_register() 函数可以注册任意数量的自动加载器,当使用尚未被定义的类(class)和接口(interface)时自动去加载。通过注册自动加载器,脚本引擎在 PHP 出错失败前有了最后一个机会加载所需的类。
<?php
spl_autoload_register(function ($class_name) {
require_once $class_name . '.php';
});
$obj = new MyClass1();
$obj2 = new MyClass2();
?>
重载
PHP所提供的重载(overloading)是指动态地创建类属性和方法。我们是通过魔术方法(magic methods)来实现的。
属性重载
-
在给不可访问(protected 或 private)或不存在的属性赋值时,__set() 会被调用。
-
读取不可访问(protected 或 private)或不存在的属性的值时,__get() 会被调用。
-
当对不可访问(protected 或 private)或不存在的属性调用 isset() 或 empty() 时,__isset() 会被调用。
-
当对不可访问(protected 或 private)或不存在的属性调用 unset() 时,__unset() 会被调用。
参数 $name 是指要操作的变量名称。__set() 方法的 $value 参数指定了 $name 变量的值。
方法重载
-
在对象中调用一个不可访问方法或不存在的方法时,__call() 会被调用。
-
在静态上下文中调用一个不可访问方法或不存在的方法时,__callStatic() 会被调用。
后期静态绑定
- self:它会调用当前类(current class)的方法
- static:延迟静态绑定意味着,当我们用static关键字调用一个继承方法时,它将在运行时绑定调用类(calling class)。
self实例
class Car
{
public static function model()
{
self::getModel();
}
protected static function getModel()
{
echo "I am a Car!";
}
}
class Mercedes extends Car
{
protected static function getModel()
{
echo "I am a Mercedes!";
}
}
$a = new Mercedes();
$a::model(); //输出I am a Car
static实例
class Car
{
public static function model()
{
static::getModel();
}
protected static function getModel()
{
echo "I am a Car!";
}
}
class Mercedes extends Car
{
protected static function getModel()
{
echo "I am a Mercedes!";
}
}
$a = new Mercedes();
$a::model(); //输出I am a Mercedes
对象序列化
serialize()
:将对象转换成一个包含字节流的字符串unserialize()
:重新把字符串变回php原来的值。反序列化只有该文件定义了此类才可以,也可以使用自动加载autoload来解决__sleep()
:序列化时会触发__wakeup()
:反序列化时会触发
stdClass
通用空类,json_decode
可以创建此类,la框架经常使用到
new \stdClass();
$obj = (object) array('foo' => 'bar');
var_dump($obj);
$json = '{"foo":"bar"}';
var_dump(json_decode($json));
生成器类
todo
对象和引用
默认情况下对象是通过引用传递的。其实不是完全正确
php的引用(别名),两个不同的变量ab名指向相同的内容,也叫做b变量是a变量的别名。在php中,一个对象变量不再保存整个对象的值。只是保存一个标识符来访问真正的对象内容。当对象作为参数传递,作为结果返回,或赋值给另外一个变量,另外一个变量跟原来的不是引用关系,只是他们都保存着相同一个标识符的拷贝,这个标识符指向同一个对象的真正内容
对象引用案例
<?php
# 对象引用案例
class SimpleClass {
}
$a = new SimpleClass(); // new创建了一个对象
$b = $a; // a赋值给b,此时a,b都不是引用,ab只是指向了同一个标识符,此标识符指向了真正的内容(这个用法和 “对象 当作函数的参数传递” 一样)
$c = &$a; // a,c都是引用,引用的概念:c只是a的一个别名
$a->var = 'a,b,c都会改变'; // a标识符指向的内容改变
$b->var = 'a,b,c都会改变'; // a标识符指向的内容改变
$c->var = 'a,b,c都会改变'; // a标识符指向的内容改变
$a = null; // a 和 a的别名c不再指向那个标识符
var_dump($a); // print null a指向null
var_dump($c); // print null c指向的a是null,所以是空
var_dump($b); // print class 所以b非空
?>
其他案例
<?php
$a = 1;
$b = &$a; //b引用a,b是a的别名
$c = $b; //b是a的别名,c并不是引用,b和c指向同一个标识符,标识符指向了val=1(b是别名把a的值赋值给了c)
$a = null; //更改a 会更改到 a的别名b, c还是指向原来的标识符
var_dump($a); // null
var_dump($b); // null
var_dump($c); // 1
枚举
基本用法
基础枚举
<?php
enum Suit
{
case Hearts;
case Diamonds;
case Clubs;
case Spades;
}
// 调用
Suit::Hearts
?>
回退枚举:为了方便数据库存储,所以可以赋一个标量值,且需要指定标量值类型(string | int)
<?php
enum Suit: string
{
case Hearts = 'H';
case Diamonds = 'D';
case Clubs = 'C';
case Spades = 'S';
}
// 调用
Suit::Hearts
?>
错误error
可以通过下面两种发放修改错误级别
php.ini
内的error_reporting
- 运行时调用
error_reporting()
防范
php7以后,大部分error是可以通过try catch捕获到
异常
通过try
监听代码块,throw
抛出异常,catch
捕获异常。
如果异常函数内没有catch,会通过调用栈,向上冒泡匹配catch,如果还是没有则会fatal error。
纤程Fiber
纤程(Fiber)表示一组有完整栈,可中断的功能
生成器
生成器基本使用
生成器提供了一种更容易的方法来实现简单的对象迭代(可以被遍历的对象),相比较定义类实现 Iterator 接口的方式,性能开销和复杂性大大降低。
<?php
function xrange($start, $limit, $step = 1) {
if ($start <= $limit) {
if ($step <= 0) {
throw new LogicException('Step must be positive');
}
for ($i = $start; $i <= $limit; $i += $step) {
yield $i;
}
} else {
if ($step >= 0) {
throw new LogicException('Step must be negative');
}
for ($i = $start; $i >= $limit; $i += $step) {
yield $i;
}
}
}
/*
* 注意下面range()和xrange()输出的结果是一样的。
*/
echo 'Single digit odd numbers from range(): ';
foreach (range(1, 9, 2) as $number) {
echo "$number ";
}
echo "\n";
echo 'Single digit odd numbers from xrange(): ';
foreach (xrange(1, 9, 2) as $number) {
echo "$number ";
}
?>
yield 关键字
生成器函数的核心是yield关键字。它最简单的调用形式看起来像一个return申明,不同之处在于普通return会返回值并终止函数的执行,而yield会返回一个值给循环调用此生成器的代码并且也不会结束生成器函数(我执行的和官方说的不一致啊)
yield成成键值对
<?php
/*
* 下面每一行是用分号分割的字段组合,第一个字段将被用作键名。
*/
$input = <<<'EOF'
1;PHP;Likes dollar signs
2;Python;Likes whitespace
3;Ruby;Likes blocks
EOF;
function input_parser($input) {
foreach (explode("\n", $input) as $line) {
$fields = explode(';', $line);
$id = array_shift($fields);
yield $id => $fields;
}
// 上面foreach结束并不会关闭函数,还是会执行下方yield
yield "Go" => "channel,goroutine";
}
foreach (input_parser($input) as $id => $fields) {
echo "$id:\n";
echo " $fields[0]\n";
echo " $fields[1]\n";
}
?>
yield from
从其他生成器中获得值
<?php
function count_to_ten() {
yield 1;
yield 2;
yield from [3, 4];
yield from new ArrayIterator([5, 6]);
yield from seven_eight();
yield 9;
yield 10;
}
function seven_eight() {
yield 7;
yield from eight();
}
function eight() {
yield 8;
}
foreach (count_to_ten() as $num) {
echo "$num ";
}
?>
生成器和iterator对象
生成器语法简单,不用去实现一个Iterator对象。但是只能向前迭代,不发乱序遍历
注解
注解功能使得代码中的声明部分都可以添加结构化、机器可读的元数据, 注解的目标可以是类、方法、函数、参数、属性、类常量。
通过 反射reflection
API 可在运行时获取注解所定义的元数据。
php8注解语法
#[class(param)]
可用于初始化,权限判断,路由注册等功能。
使用步骤:
- 声明注解类(业务逻辑写里面),他一定是继承
\Attribute
类 - 给需要用到此类逻辑的 类,方法,函数,参数,属性等地方添加上注解
- 使用反射reflection,触发注解
下面是路由注册 使用注解实现的简单实现
注解类 Route.php
<?php
namespace App\Route\Attributes;
use Attribute;
// 声明该Route注解可以重复使用,只能绑定在类方法上
#[Attribute(Attribute::IS_REPEATABLE|Attribute::TARGET_METHOD)]
class Route{
public static $all = [];
public string $path = ""; // 路由地址
public string $method = "GET"; // 路由请求方法
public string $controller = ""; // 路由指向的控制器
public string $function = ""; // 路由指向的控制器的方法
public function __construct()
{
}
/**
* @description: 设置路由地址
* @param string $path
* @return self
*/
public function setPath (string $path) :self
{
$this->path = $path;
return $this;
}
/**
* @description: 设置控制器
* @param string $controller
* @return self
*/
public function setController (string $controller) :self
{
$this->controller = $controller;
return $this;
}
/**
* @description: 设置请求方法
* @param string $method
* @return self
*/
public function setMethod (string $method) :self
{
$this->method = $method;
return $this;
}
/**
* @description: 设置绑定的function
* @param string $function
* @return self
*/
public function setFunction (string $function) :self
{
$this->function = $function;
return $this;
}
/**
* @description: 添加路由
* @return void
*/
public function addRoute() :void
{
self::$all[] = $this;
}
}
controller标记注解 TestController.php
<?php
namespace App\Controller;
use App\Route\Attributes\Route;
class TestController
{
#[Route('/test/route','GET')]
#[Route('/route/test','POST')]
public function testRoute() {
// do something here
}
// #[Route('/route/route123','POST')]
public function route123() {
// do something here
}
}
触发注解 index.php
<?php
// 简单的autoload
$call_back = function ($className) {
$file = "./".pathinfo($className)['filename'].".php";
require_once $file;
};
spl_autoload_register($call_back);
// reflect获取注解
function getAttributeData($reflection)
{
$controller = $reflection->getName();
// 如果注解是直接绑定在类上面,那么直接从类的反射获取注解
// $attributes = $reflection->getAttributes(App\Route\Attributes\Route::class);
$methods = $reflection->getMethods();
// 因为注解是绑定在方法上的,因此循环方法,获取方法的注解
foreach($methods as $method){
// 方法名
$function = $method->getName();
// 指定获取某个注解的数据
$attributes = $method->getAttributes();
// var_dump($attributes11);
// $attributes = $method->getAttributes(App\Route\Attributes\Route::class);
var_dump($attributes);
foreach ($attributes as $attribute) {
// 拿到一个新的 Route 实例
$route = $attribute->newInstance();
// 拿到注解上的参数
$params = $attribute->getArguments();
/**
* [
* ['/test/route','GET'],
* ['/route/test','POST']
* ]
*/
// 执行路由添加
$route->setController($controller)->setFunction($function)
->setPath($params[0])->setMethod($params[1])->addRoute();
}
}
}
// 对 TestController 使用反射,并执行注解生效的方法
getAttributeData(new ReflectionClass(App\Controller\TestController::class));
var_dump(App\Route\Attributes\Route::$all);
引用
在 PHP 中引用意味着用不同的名字访问同一个变量内容。这并不像 C 的指针:例如你不能对他们做指针运算,他们并不是实际的内存地址。
-
引用是符号表别名。
-
引用不是指针
具体实例可以看此篇上面章节的: 对象和引用
预定义接口和类
Traversable(遍历接口)
检测一个类是否可遍历
它里面没有任何方法,作用仅仅是作为所有可遍历类的基本既接口
interface Traversable {
}
Iterator迭代器接口
可在内部迭代自己的外部迭代器或类的接口
php提供了用于日常任务的迭代器,spl迭代器
全靠自己实现
interface Iterator extends Traversable {
/* 方法 */
public current(): mixed
public key(): mixed
public next(): void
public rewind(): void
public valid(): bool
}
Throwable 异常接口
Throwable 是能被 throw
语句抛出的最基本的接口(interface),包含了 Error 和 Exception 。
一般都是直接继承\Exception
类
ArrayAccess (数组形式访问类)接口
提供像访问数组一样访问对象的能力的接口。
interface ArrayAccess {
/* 方法 */
public offsetExists(mixed $offset): bool
public offsetGet(mixed $offset): mixed
public offsetSet(mixed $offset, mixed $value): void
public offsetUnset(mixed $offset): void
}
Serializable 序列化接口
继承了此接口,不再调用魔术方式__wake()
和__sleep()
interface Serializable {
/* 方法 */
public serialize(): ?string
public unserialize(string $data): void
}
Closure 匿名类
匿名类,匿名函数内部会产生Closure对象。不允许修改此类的,有final
关键字。
全是c实现的
final class Closure {
/* 方法 */
private __construct()
public static bind(Closure $closure, ?object $newThis, object|string|null $newScope = "static"): ?Closure
public bindTo(?object $newThis, object|string|null $newScope = "static"): ?Closure
public call(object $newThis, mixed ...$args): mixed
public static fromCallable(callable $callback): Closure
}
stdClass
通用类,是一个空类,用于给用户一个临时对象,可以创建动态属性。
有几个php函数也会创建此类实例,例如json_decode()
,mysqli_fetch_object
或PDOStatment::fetchObject
class stdClass {
}
Generator 生成器类
Generator 对象是从生成器语法(此文上面的生成器类)生成的
Fiber 纤程类
WeakMap类
可将对象作为map字典的key来使用
<?php
$wm = new WeakMap();
$o = new StdClass;
class A {
public function __destruct() {
echo "Dead!\n";
}
}
$wm[$o] = new A;
var_dump(count($wm));
echo "Unsetting...\n";
unset($o);
echo "Done\n";
var_dump(count($wm));
context上下文参数和选项
socket套接字上下文
套接字上下文选项可用于所有工作在套接字上的封装协议,像 tcp
, http
和 ftp
。
可选项:
- bindto : 绑定接听ip端口
- backlog:用于限制socket监听队列中未完成连接的数量,tcp的syn队列(半连接队列)
- ipv6_v6only:使用ipv6
- so_reuseport:即使来自不同的进程,也能对同一个ip:port对进行多个绑定(重复使用port)
- so_broadcast:允许向广播地址发送数据,从广播地址接收数据
- tcp_nodelay:设置此选项为
true
将相应地设置SOL_TCP,NO_DELAY=1
, 从而禁用 TCP Nagle 算法。(tcp的啥算法不清楚,todo)
<?php
// 使用 IP '192.168.0.100' 连接到互联网
$opts = array(
'socket' => array(
'bindto' => '192.168.0.100:0',
),
);
// 创建上下文...
$context = stream_context_create($opts);
// ...并使用它来读取数据
echo file_get_contents('http://www.example.com', false, $context);
http context
提供给 http://
和 https://
传输协议的 context 选项
- method string:get , post ,put等http方法
- header array:,请求头定义
- user_agent string:header头中的用户代理
- content string: 发送数据
- proxy string:代理ip地址
- request_fulluri bool:当设置为
true
时,在构建请求时将使用整个 URI 。(例如:GET http://www.example.com/path/to/file.html HTTP/1.0
) - follow_location int:跟随
Location
header 的重定向。设置为0
以禁用。 - max_redirects int:最大重定向数
- protocal_version float:http版本
- time_out float:超时时间
- ignore_error bool:无视故障状态码
<?php
$postdata = http_build_query(
array(
'var1' => 'some content',
'var2' => 'doh'
)
);
$opts = array('http' =>
array(
'method' => 'POST',
'header' => 'Content-type: application/x-www-form-urlencoded',
'content' => $postdata
)
);
$context = stream_context_create($opts);
$result = file_get_contents('http://example.com/submit.php', false, $context);
?>
ftp
ftp://
和 ftps://
传输的上下文选项。
ssl
ssl://
和 tls://
传输协议上下文选项清单。
curl
CURL 上下文选项在 CURL 扩展被编译
phar
phar://
封装(wrapper)的上下文(context)选项。
用于压缩php文件至phar中,composer.phar同理
具体实例:https://www.nhooo.com/note/qa0py3.html
zip
Zip 上下文选项可用于 zip
包装器。
- password:用于加密归档的密码
<?php
// 读取加密归档
$opts = array(
'zip' => array(
'password' => 'secret',
),
);
// 创建上下文...
$context = stream_context_create($opts);
// ...并且使用它读取数据
echo file_get_contents('zip://test.zip#test.txt', false, $context);
?>
php支持的协议和自己封装协议
PHP 带有很多内置 URL 风格的封装协议,可用于类似 fopen()、 copy()、 file_exists() 和 filesize() 的文件系统函数。 除了这些封装协议,还能通过 stream_wrapper_register() 来注册自定义的封装协议。
- file:// — 访问本地文件系统
- http:// — 访问 HTTP(s) 网址
- ftp:// — 访问 FTP(s) URLs
- php:// — 访问各个输入/输出流(I/O streams)
- zlib:// — 压缩流
- data:// — 数据(RFC 2397)
- glob:// — 查找匹配的文件路径模式
- phar:// — PHP 归档
- ssh2:// — 安全外壳协议 2
- rar:// — RAR
- ogg:// — 音频流
- expect:// — 处理交互式的流
php://
php:// — 访问各个输入/输出流(I/O streams)
- php://stdin, php://stdout 和 php://stderr:允许直接访问 PHP 进程相应的输入或者输出流
- php://input :可以访问请求的原始数据的只读流
- php://output:是一个只写的数据流,使用
print
,echo
一样的方式写入输出缓冲区 - php://fd:php://fd 允许直接访问指定的文件描述符。例如 php://fd/3 引用了文件描述符 3。
- php://memory 和 php://temp:允许读写临时数据
- php://filter:
<?php
// 获取请求数据
file_get_content("php://input");
// 限制内存为 5 MB。
$fiveMBs = 5 * 1024 * 1024;
$fp = fopen("php://temp/maxmemory:$fiveMBs", 'r+');
fputs($fp, "hello\n");
// 读取刚才写入的内容。
rewind($fp);
echo stream_get_contents($fp);
?>
特点
用php 进行http认证
添加http向响应头,向客户端浏览器发送“Authentication Required
”信息,使其弹出一个用户名/密码输入窗口。
用户输入用户名和密码后,包含有 URL 的 PHP 脚本将会加上预定义变量 $_SERVER[PHP_AUTH_USER]
,$_SERVER[PHP_AUTH_PW]
和 $_SERVER[AUTH_TYPE]
被再次调用,这三个变量分别被设定为用户名,密码和认证类型。
仅 支持 “Basic” 和 “Digest” 认证方法。
http basic 和 digest鉴权详解https://www.cnblogs.com/lsdb/p/10621940.html
文件上传处理
POST上传方法
本特性可以使用户上传文本和二进制文件。用 PHP 的认证和文件操作函数,可以完全控制允许哪些人上传以及文件上传后怎样处理。
全局变量 $_FILES 包含有所有上传的文件信息。
PUT上传
PHP 对部分客户端具备的 HTTP PUT 方法提供了支持。PUT 请求比文件上传要简单的多,它们一般的形式为:
PUT /path/filename.html HTTP/1.1
<?php
/* php://input流模式 */
$putdata = fopen("php://input", "r");
/* Open a file for writing */
$fp = fopen("myputfile.ext", "w");
/* Read the data 1 KB at a time
and write to the file */
while ($data = fread($putdata, 1024))
fwrite($fp, $data);
/* Close the streams */
fclose($fp);
fclose($putdata);
?>
数据库库持久连接
持久的数据库连接是指在脚本结束运行时不关闭的连接。当收到一个持久连接的请求时。PHP 将检查是否已经存在一个(前面已经开启的)相同的持久连接。如果存在,将直接使用这个连接;如果不存在,则建立一个新的连接。所谓“相同”的连接是指用相同的用户名和密码到相同主机的连接。
web服务器把php当作的一个模块。web服务器是一个多进程模型,主进程处理连接,子进程处理业务调用mysqli_pconnect
,子进程处理业务后不释放进程,也不断开mysql连接。如果碰巧遇到同一个连接进到同一个进程则可以直接使用。如果mysql设置的最大连接数是100,而又有101个请求过来,并且都没释放mysql,那么就会导致拒绝连接,所以不推荐使用
函数参考
影响php行为的扩展
APCU
apcu是基于共享内存技术建设的,多个cgi之间访问apcu中的cache可以完全等同于访问自己进程的一块内存一样,不需要发任何的网络请求。但是他局限于单机。
Componere
final
类在php语法内是不允许被修改的。Componere在运行时提供 修改和继承final类的方法。
err和log
- debug_backtrace — 产生一条回溯跟踪(backtrace)
- debug_print_backtrace — 打印一条回溯。
- error_clear_last — 清除最近一次错误
- error_get_last — 获取最后发生的错误
- error_log — 发送错误信息到某个地方
- error_reporting — 设置应该报告何种 PHP 错误
- restore_error_handler — 还原之前的错误处理函数
- restore_exception_handler — 恢复之前定义过的异常处理函数。
- set_error_handler — 设置用户自定义的错误处理函数
- set_exception_handler — 设置用户自定义的异常处理函数
- trigger_error — 产生一个用户级别的 error/warning/notice 信息
- user_error — 别名 trigger_error
FFI
允许在php文件中 加载 共享库(.DLL 或 .so) 或 调用C函数 或访问C数据结构。一般都不会用上的,需要会写C
OPcache
OPcache 通过将 PHP 脚本预编译的字节码存储到共享内存中来提升 PHP 的性能, 存储预编译字节码的好处就是 省去了每次加载和解析 PHP 脚本的开销。
直接设置php.ini开启,并指定目录
output contral 输出控制
- 在PHP中,像header(), session_start(), setcookie() 等这样的发送头文件的函数前,不能有任何的输出,而利用输出缓冲控制函数可以在这些函数前进行输出而不报错。其实这么做没啥必要,非常少见的用法。
- 对输出的内容进行处理,例如生成静态缓存文件、进行gzip压缩输出,这算是较常用的功能了,后面会有详细介绍。
- 捕获一些不可获取的函数输出,例如phpinfo(), var_dump() 等等,这些函数都会将运算结果显示在浏览器中,而如果我们想对这些结果进行处理,则用输出缓冲控制函数是个不错的方法。说的通俗点,就是这类函数都不会有 返回值,而要获取这些函数的输出数据,就要用到输出缓冲控制函数。