PHP基础参考03-基础语法参考2
目录
函数
-
用户自定义函数
-
通常情况下,函数无需在调用前定义,除了有条件定义函数。
-
有条件定义的函数
除非有条件定义的函数。
//one();//这里就还不能调用函数one,因为one函数还不存在。 two();//这里是可以调用two函数的,定义一般在编译后就存在。 if(xx){ function one(){ //... } } one(); function two(){ }
还有一种有条件定义函数,就是函数中的函数,函数中的函数,也是一个令phper意外的语法:
function outer(){ function inner(){ } } //inner();这里还不能调用inner函数,因为还不存在 outer();//调用outer函数后,才真正定义了inner函数 inner();
-
作用域。php的函数和类都具有全局作用域。
不论定义在哪里,只要加载可访问即可调用。
-
PHP 不支持函数重载,也不可能取消定义或者重定义已声明的函数。
-
递归函数
在php中,我们除了要注意无限递归,还要避免递归调用超过100-200层,因为可能会使堆栈崩溃从而使当前脚本终止。
-
-
函数参数
-
函数参数默认传值方式
-
允许参数默认值
-
可变数量的参数列表
PHP 在用户自定义函数中支持可变数量的参数列表。由 … 语法实现函数接收参数列表。
注意:旧php版本经常使用这些函数来获取可变参数 func_num_args()、 func_get_arg() 和 func_get_args(),但我们不再建议使用此方式,以后建议使用 … 来替代。
function sum(...$numbers) { $acc = 0; foreach ($numbers as $n) { $acc += $n; } return $acc; } echo sum(1, 2, 3, 4);
灵活起来,扩展起来,胆大心细用起来。
-
… 语法还可以用来传递参数
主要用于传递数组,映射到函数参数列表
function add($a, $b) { return $a + $b; } echo add(...[1, 2])."\n"; $a = [1, 2]; echo add(...$a);
-
php8有个逗号的小细节,最后一个参数尾部可以追加逗号,这个逗号会被忽略,只为了方便垂直的书写很多的参数。
function a($aaa, $bbb, $ccc, $ddd, $eee, $fff, ){ //do something }
-
-
返回值
-
省略return。PHP中如果省略了 return,则返回值为 null。
-
返回多个值。php不支持返回多个值,可以考虑返回数组等符合类型。
-
返回一个引用。
没有深刻本质的理解引用的话,慎用函数返回引用。
function &returns_reference() { return $someref; } $newref =& returns_reference();
-
-
可变函数
PHP 支持可变函数的概念。
也就是说一个变量名后有圆括号(),PHP 将寻找与变量的值同名的函数,并且尝试执行它。可变函数可以用来实现包括回调函数,函数表在内的一些用途。
经常用于回调函数。
function a($info,$callback){ function_exists($callback) && $callback($info); } function b($data){ var_dump($data); } a([1,2],"b");
可变函数,支持函数也支持类中的方法调用。
-
内置函数
php内置很多很好用的函数,也有一些需要扩展才能用的函数,比如image、mysqli等。
我们只想吐槽的是:php的内置函数总让我们很难记得住,我们觉得一些原因是参数原则、返回原则不统一导致的,我们有时不清楚这个函数是否有返回值,还是直接作用于参数变量上。
-
匿名函数
匿名函数也叫闭包函数,有javascript爱好的小伙伴,肯定很熟悉,也很经常使用。
匿名函数目前是通过 Closure 类来实现的
闭包还可以作为变量的值来使用,和可变函数相似。
常用于回调。
-
箭头函数
箭头函数是 PHP 7.4 的新语法,是一种更简洁的 匿名函数 写法。
箭头函数的基本语法为
fn (argument_list) => expr
箭头函数支持与 匿名函数 相同的功能,只是其父作用域的变量总是自动的
$y = 1; $fn1 = fn($x) => $x + $y; // 相当于 using $y by value: $fn2 = function ($x) use ($y) { return $x + $y; };
类与对象
很多人都停留在php是过程语言,不能面向对象,php已经8了,在5就已经逐步与面向对象接轨了。
PHP 具有完整的对象模型。特性包括: 访问控制,抽象类和 final 类与方法,附加的魔术方法,接口,对象复制。
对于面向对象的基本知识,不需要我们介绍了,大家都懂。
-
final
被继承的方法和属性可以通过用同样的名字重新声明被覆盖。但是如果父类定义方法时使用了 final,则该方法不可被覆盖。可以通过 parent:: 来访问被覆盖的方法或属性。
注意: 属性不能被定义为 final,只有类和方法才能被定义为 final。
基本的知识,但很少人能用上,也许是场景不需要用到,但也许哪天就真香,时刻牢记。
-
var
虽然有些版本允许使用var定义属性,会被视为public,但我们不建议使用。
-
const常量
常量的值必须是一个定值,不能是变量,类属性,数学运算的结果或函数调用。
要区别于外部const/define定义的常量。
PHP 7.1.0 开始,类的常量可以定义为公有、私有或受保护。如果没有设置这些关键字,则该常量默认为公有,也就是之前的版本只能使用const关键字定义常量,默认这些常量是公开可访问的,7.1之后允许public、protect、private的访问控制关键字。
-
类自动加载
现代php框架都引入自动加载机制了,特别是composer第三方库使用。
spl_autoload_register() 函数可以注册任意数量的自动加载器,当使用尚未被定义的类(class)和接口(interface)时自动去加载。
不建议使用__autoload函数,后续版本会被弃用。
spl_autoload_register函数用于注册触发加载类,提供回调加载,达到按需加载。
spl_autoload_register(function ($class_name) { require_once $class_name . '.php'; }); $obj = new MyClass1(); $obj2 = new MyClass2();
-
构造函数与析构函数
-
子类的构造函数不会默认或隐性的调用父类构造函数,有需要时,要手动调用父类构造函数。
-
析构函数即使在使用 exit() 终止脚本运行时也会被调用。但在析构函数中调用 exit() 将会中止其余关闭操作的运行。
-
在析构函数(在脚本终止时被调用)中抛出一个异常会导致致命错误。
-
-
::,只是认识下这个符号叫:范围解析操作符
在访问类常量、静态属性、静态方法,我们常会用到双冒号,也就是范围解析操作符。
-
关于抽象类和接口
如果对抽象类和接口有不清楚的可以看官方文档介绍
-
trait
Trait 是为类似 PHP 的单继承语言而准备的一种代码复用机制。Trait 为了减少单继承语言的限制,使开发人员能够自由地在不同层次结构内独立的类中复用 method。
Trait 和 Class 相似,但仅仅旨在用细粒度和一致的方式来组合功能。 无法通过 trait 自身来实例化。它为传统继承增加了水平特性的组合;也就是说,应用的几个 Class 之间不需要继承。
- 优先顺序是当前类中的方法会覆盖 trait 方法,而 trait 方法又覆盖了基类中的方法。
class Base { public function sayHello() { echo 'Hello '; } } trait HelloWorld { public function sayHello() { echo 'Hello World!'; } } class MyHelloWorld extends Base { use HelloWorld; } $o = new MyHelloWorld(); $o->sayHello(); //输出:Hello World! //因为trait覆盖了基类的方法 class TheWorldIsNotEnough { use HelloWorld; public function sayHello() { echo 'Hello Universe!'; } } $o = new TheWorldIsNotEnough(); $o->sayHello(); //输出:Hello Universe! //因为当前类覆盖trait的方法
-
一个类中支持use多个trait,用逗号隔开
-
方法冲突解决
如果两个 trait 都插入了一个同名的方法,如果没有明确解决冲突将会产生一个致命错误。
为了解决多个 trait 在同一个类中的命名冲突,需要使用 insteadof 操作符来明确指定使用冲突方法中的哪一个。
以上方式仅允许排除掉其它方法,as 操作符可以 为某个方法引入别名。 注意,as 操作符不会对方法进行重命名,也不会影响其方法。
use A, B { B::smallTalk insteadof A;//相当于排除A中的smallTalk A::bigTalk insteadof B; B::bigTalk as talk;//定义了talk别名 }
-
修改use trait中的方法访问控制
直接看范例:
// 修改 sayHello 的访问控制 class MyClass1 { use HelloWorld { sayHello as protected; } } // 给方法一个改变了访问控制的别名 // 原版 sayHello 的访问控制则没有发生变化 class MyClass2 { use HelloWorld { sayHello as private myPrivateHello; } }
-
trait组合
trait和class一样也可以组合其他多个trait。
-
trait支持抽象方法,用于强制实体类实现trait的抽象方法
-
trait支持静态方法
-
trait支持属性,但注意冲突,一般是不能定义同样名称的属性。我们建议避免trait和class出现相同名称的属性,即使访问可见性、初始值都一样(允许的),我也不建议,会导致混乱。
-
匿名类
php7开始支持匿名类,用于创建一次性简单对象。
$util->setLogger(new class { public function log($msg) { echo $msg; } }); //也就是不需要先定义一个logger的对象,再new给setLogger方法。
-
魔术方法
PHP所提供的重载(overloading)是指动态地创建类属性和方法。我们是通过魔术方法(magic methods)来实现的。
PHP中的重载与其它绝大多数面向对象语言不同。传统的重载是用于提供多个同名的类方法,但各方法的参数类型和个数不同。
-
clone
对象复制可以通过 clone 关键字来完成(如果可能,这将调用对象的 __clone() 方法)。
当对象被复制后,PHP 5 会对对象的所有属性执行一个浅复制(shallow copy)。所有的引用属性 仍然会是一个指向原来的变量的引用。如果有少量及简单的引用属性,可以在魔术方法__clone中手动实现引用属性的复制。
class AA{ public $object1; function __clone() { // 强制复制一份this->object, 否则仍然指向同一个对象 $this->object1 = clone $this->object1; } } $a = new AA(); $a2 = clone $a;//通过__clone方法,达到对$a对象的深拷贝。
-
static::后期静态绑定
self、parent、static
我们都知道parent的用法,就是从当前类(子类)往上追溯基类的存在的这个方法;static则是从当前类(父类、基类)往下追溯至当前执行实例(子类)中存在的方法;
class A { public static function who() { echo __CLASS__; } public static function test() { static::who(); // 后期静态绑定从这里开始,这里会执行调用test方法的实例的who方法,也就是B类实例的who方法。 self::who();//这里只会调用当前类中的who方法,如果有的话,也就是当前代码行所在类A parent::who(); } } class B extends A { public static function who() { echo __CLASS__; } } B::test();//输出:BA
-
对象与引用 及 传址赋值与引用赋值
在php5 的对象编程经常提到的一个关键点是“默认情况下对象是通过引用传递的”。但其实这不是完全正确的。
PHP 的引用是别名,就是两个不同的变量名字指向相同的内容。
在 PHP 5以后,一个对象变量已经不再保存整个对象的值。而是保存一个标识符来访问真正的对象内容。
当对象作为参数传递,作为结果返回,或者赋值给另外一个变量,另外一个变量跟原来的不是引用的关系,只是他们都保存着同一个标识符的拷贝,这个标识符指向同一个对象的真正内容。
这标识符我们可以理解为存储地址,所以php的对象正常情况下的复制、传递都是通过传址的方式,而不是引用,虽然效果是一样的。
class A { public $foo = 1; } $a = new A; $b = $a; // $a ,$b都是同一个标识符的拷贝,意味着a、b指向同一个地址 // ($a) = ($b) = <id> $b->foo = 2; echo $a->foo."\n";//输出:2 $c = new A; $d = &$c; // $c ,$d是引用,d是c的一个别名,其实他们还是一样的 // ($c,$d) = <id> $d->foo = 2; echo $c->foo."\n";//输出:2 $e = new A; function foo($obj) {//e和obj都是同一个标识符的拷贝 // ($obj) = ($e) = <id> $obj->foo = 2; } foo($e); echo $e->foo."\n";//输出:2
引用
-
引用理解
在 PHP 中引用意味着用不同的名字访问同一个变量内容。
这并不像 C 的指针:例如你不能对他们做指针运算,他们并不是实际的内存地址。
引用是符号表别名。
注意:变量名和变量内容是不一样的, 因此同样的内容可以有不同的名字。
最接近的比喻是 Unix 的文件名和文件本身——变量名是目录条目,而变量内容则是文件本身。引用可以被看作是 Unix 文件系统中的硬链接hard link。
-
引用赋值
看一个引用赋值例子:
$a =& $b;
注意:$a 和 $b 在这里是完全相同的,这并不是 $a 指向了 $b 或者相反,而是 $a 和 $b 指向了同一个地方, $a 和 $b 指向了同一个变量容器。
-
引用传参
看一个引用传参例子:
function foo(&$var) { $num = 1; $var =& $num;//这里改变了$var,$var不再作为传参$a的引用别名,而变成了$num的引用别名了,也就和$a无关系了 // $var = $num;//这里是传值赋值,如果没有上一句$var =& $num,则将改变$var与$a的值,因为$var还是传参$a的引用别名。 echo $num."\n"; } $a = 2; foo($a); echo $a."\n";
如果是
$var = $num;
则输出:1 1如果是
$var =& $num;
则输出:1 2 -
引用返回
不要用返回引用来增加性能,php引擎足够聪明来自己进行优化。仅在有合理的技术原因时才返回引用!
要在方法或函数用&指明要返回引用,在调用地方通过&指明获得引用。
... public $value = 20; public function &getValue() { return $this->value; } ... $myValue = &$obj->getValue();//$myValue将绑定类中的属性$value,修改类中的$value值,也就是修改了$myValue
-
取消引用
$a = 1; $b =& $a; unset($a);
由于有一个值同时被$a和$b绑定,所以这里只是解除了$a对这个值的绑定/引用,这个值还有一个引用$b指向值,所以值不会被销毁,只是取消了$a这个引用。如果再unset($b),就再也没有引用指向这个值了,这个时候引用$b取消了,值也同时被销毁了。
如果你对unix系统下的文件硬链接、软链接足够熟悉,就能很好理解了。
-
global其实是引用
global $var
等同于var $var =& $GLOBALS['var']
所以unset($var),并不会unset全局变量,$GLOBALS[‘var’]仍然存在。
-
$this是调用他的对象的引用
命名空间
-
命名空间可以类比unix系统文件路径。在操作系统中文件有文件路径,用于区分相同文件名的不同文件。命名空间接地气点解释就是类库、类、函数等的一个路径,解决命名冲突,提高代码可读性。
-
虽然任意合法的PHP代码都可以包含在命名空间中,但只有以下类型的代码受命名空间的影响,它们是:类(包括抽象类和traits)、接口、函数和常量。
namespace MyProject\Sub\Level; const CONNECT_OK = 1; class Connection { /* ... */ } function connect() { /* ... */ }
//namespace除了支持类、接口外还支持普通函数与脚本中常量定义 require_once 'functions.php';//functions.php定义namespace为namespace extend;,且定义cli_log函数 \extend\cli_log(__FILE__."\t".__LINE__);//来自functions.php的函数 cli_log("我是谁,我来自哪里,我要做什么");//当前文件下函数 function cli_log($msg){ echo $msg." -- from local file function.\n"; }
-
同一个命名空间可以定义在多个文件中,即允许将同一个命名空间的内容分割存放在不同的文件中。
-
一个文件中定义多个命名空间
namespace MyProject\Sub\Level { const CONNECT_OK = 1; class Connection { /* ... */ } function connect() { /* ... */ } } namespace AnotherProject { const CONNECT_OK = 1; class Connection { /* ... */ } function connect() { /* ... */ } }
上面的例子创建了常量MyProject\Sub\Level\CONNECT_OK,类 MyProject\Sub\Level\Connection和函数 MyProject\Sub\Level\connect。
还有另外一个命名空间:AnoterhProject。
不建议在一个文件中定义多个命名空间。
-
命名空间使用
命名空间的使用也可以类比unix系统文件路径使用。
相对路径、绝对路径
namespace A\B\C; /* 这个函数是 A\B\C\fopen */ function fopen() { /* ... */ $f = \fopen(...); // 调用全局的fopen函数 return $f; }
namespace A\B\C; class Exception extends \Exception {} $a = new Exception('hi'); // $a 是类 A\B\C\Exception 的一个对象 $b = new \Exception('hi'); // $b 是类 Exception 的一个对象 $c = new ArrayObject; // 致命错误, 找不到 A\B\C\ArrayObject 类
namespace A; use B\D, C\E as F; // 函数调用 foo(); // 首先尝试调用定义在命名空间"A"中的函数foo() // 再尝试调用全局函数 "foo" \foo(); // 调用全局空间函数 "foo" my\foo(); // 调用定义在命名空间"A\my"中函数 "foo" F(); // 首先尝试调用定义在命名空间"A"中的函数 "F" // 再尝试调用全局函数 "F" // 类引用 new B(); // 创建命名空间 "A" 中定义的类 "B" 的一个对象 // 如果未找到,则尝试自动装载类 "A\B" new D(); // 使用导入规则,创建命名空间 "B" 中定义的类 "D" 的一个对象 // 如果未找到,则尝试自动装载类 "B\D" new F(); // 使用导入规则,创建命名空间 "C" 中定义的类 "E" 的一个对象 // 如果未找到,则尝试自动装载类 "C\E" new \B(); // 创建定义在全局空间中的类 "B" 的一个对象 // 如果未发现,则尝试自动装载类 "B" new \D(); // 创建定义在全局空间中的类 "D" 的一个对象 // 如果未发现,则尝试自动装载类 "D" new \F(); // 创建定义在全局空间中的类 "F" 的一个对象 // 如果未发现,则尝试自动装载类 "F" // 调用另一个命名空间中的静态方法或命名空间函数 B\foo(); // 调用命名空间 "A\B" 中函数 "foo" B::foo(); // 调用命名空间 "A" 中定义的类 "B" 的 "foo" 方法 // 如果未找到类 "A\B" ,则尝试自动装载类 "A\B" D::foo(); // 使用导入规则,调用命名空间 "B" 中定义的类 "D" 的 "foo" 方法 // 如果类 "B\D" 未找到,则尝试自动装载类 "B\D" \B\foo(); // 调用命名空间 "B" 中的函数 "foo" \B::foo(); // 调用全局空间中的类 "B" 的 "foo" 方法 // 如果类 "B" 未找到,则尝试自动装载类 "B" // 当前命名空间中的静态方法或函数 A\B::foo(); // 调用命名空间 "A\A" 中定义的类 "B" 的 "foo" 方法 // 如果类 "A\A\B" 未找到,则尝试自动装载类 "A\A\B" \A\B::foo(); // 调用命名空间 "A\B" 中定义的类 "B" 的 "foo" 方法 // 如果类 "A\B" 未找到,则尝试自动装载类 "A\B"
如果不清楚的,可以参考官方文档:
-
命名空间导入与别名
use My\Full\Classname as MFC;
错误与异常
-
Error异常
PHP 7 改变了大多数错误的报告方式。不同于传统(PHP 5)的错误报告机制,现在大多数错误被作为 Error 异常抛出。
这种 Error 异常可以像 Exception 异常一样被第一个匹配的 try / catch 块所捕获。如果没有匹配的 catch 块,则调用异常处理函数(事先通过 set_exception_handler() 注册)进行处理。 如果尚未注册异常处理函数,则按照传统方式处理:被报告为一个致命错误(Fatal Error)。
Error 类并非继承自 Exception 类,所以不能用 catch (Exception $e) { … } 来捕获 Error。你可以用 catch (Error $e) { … },或者通过注册异常处理函数( set_exception_handler())来捕获 Error。
php7后我们都建议更多采用异常处理机制,但只有php8对Error异常处理才更完善,php7还是会出现一些无法抛出的我们认为是Error异常的错误。
-
预定义错误异常
这里主要介绍php7开始提供的内置Error异常,主要处理php内置的一些错误异常。但还是要注意:php7对内置的Error异常处理还不够完善,在php8会得到更完善的支持,比如
echo 1/0;
,仅警告,不会抛出Error异常。-
Exception
Exception是所有异常的基类。但Error不继承于Exception。
-
ErrorException
继承于Exception。
错误异常。
-
Error
php7版本开始提供。Error异常弥补php的内部错误采用异常机制处理,当然错误的处理仍然支持set_error_handler()自定义处理。
不继承于Exception。实现了Throwable接口。
Error 是所有PHP内部错误类的基类。
-
TypeError
继承于Error。
有三种情况会抛出 TypeError。第一种,传递给函数的参数类型与函数预期声明的参数类型不匹配;第二种,函数返回的值与声明的函数返回类型不匹配;第三种,调用 PHP 内置函数时,传递了非法的数字参数(仅限在严格模式下 / strict mode)。
-
CompileError
继承于Error。
CompileError 是针对一些编译错误抛出的,之前是会发出致命错误。
-
ArgumentCountError
继承于TypeError。
当传递给用户定义的函数或方法的参数太少时被抛出。
-
ArithmeticError
继承于Error。
当执行数学运算时发生错误时被抛出。
-
AssertionError
继承于Error。
在函数 assert() 断言失败时被抛出。
-
DivisionByZeroError
继承于ArithmeticError。
当除数为零时被抛出。
try { echo 1/0; // 仅警告 intdiv(1, 0); echo 1%0; } catch (DivisionByZeroError $e) { echo $e->getMessage(); }
注意:
-
-
参考
后续我们再仔细看看错误与异常相关函数。
迭代生成器
利用生成器,php也可以做到协程的效果。
-
迭代生成器概念原理
生成器提供了一种更容易的方法来实现简单的对象迭代,相比较定义类实现 Iterator 接口的方式,性能开销和复杂性大大降低。
一个生成器被调用的时候,它返回一个可以被遍历的对象(迭代器)。当你遍历这个对象(迭代)的时候(例如通过一个foreach循环),PHP 将会在每次需要值的时候调用生成器函数,并在产生一个值之后保存生成器的状态,这样它就可以在需要产生下一个值的时候恢复调用状态。(目前看还是官方这段话解释最合适最容易理解,结合实际例子,再多看几遍。)
生成器允许你在 foreach 代码块中写代码来迭代一组数据而不需要在内存中创建一个数组, 那会使你的内存达到上限,或者会占据可观的处理时间。相反,你可以写一个生成器函数,就像一个普通的自定义函数一样, 和普通函数只返回一次不同的是, 生成器可以根据需要 yield 多次,以便生成需要迭代的值。
看一个简单例子(重写range函数):
function xrange($start, $limit, $step = 1) { for ($i = 0; $i < $limit; $i += $step) { yield $i + 1 => $i;//yield除了可以生成简单值外,还可以生成键值对 key=>value } } foreach (xrange(0, 9) as $key => $val) { printf("%d %d \n", $key, $val); } // 输出 // 1 0 // 2 1 // 3 2 // 4 3 // 5 4 // 6 5 // 7 6 // 8 7 // 9 8
-
关键字yield
生成器函数的核心是yield关键字。它最简单的调用形式看起来像一个return申明,不同之处在于普通return会返回值并
终止
函数的执行,而yield会返回一个值给循环调用此生成器的代码并且只是暂停
执行生成器函数。注意:如果在一个表达式上下文(例如在一个赋值表达式的右侧)中使用yield,你必须使用圆括号把yield申明包围起来。 例如这样是有效的:
$data = (yield $value);
-
生成器函数支持返回键值对
-
生成器函数支持返回引用
-
yield from
function three(){ yield 1; yield from two(); yield from [3,4]; } function two(){ yield 2; } foreach(three() as $num){ echo $num."\n"; }
-
send传递值
@todo
-
更多参考
注解
php8刚引入注解,在php8之前symfony、laravel、hyperf等框架都通过反射实现了所谓的“注解”,满足AOP编程。
在官方注解未出来之前,大部分框架的注解是通过反射实现,先把类或函数的注释取到,用语法解析或者正则之类的方式匹配注解符号,假装是注解,然后处理“注解”。
官方注解的符号也一直在变:从@ 到 «» 到 @@ 再到 #[]
很需要更多的应用场景来深入理解php的注解,注解目前更多用于配置去中心化。
预定义变量
超全局变量是在全部作用域中始终可用的内置变量。
-
$GLOBALS — 引用全局作用域中可用的全部变量
-
$_SERVER — 服务器和执行环境信息
-
$_GET — HTTP GET 变量
注意:GET 是通过 urldecode() 传递的。
-
$_POST — HTTP POST 变量
当 HTTP POST 请求的 Content-Type 是 application/x-www-form-urlencoded 或 multipart/form-data 时,会将变量以关联数组形式传入当前脚本。
-
$_FILES — HTTP 文件上传变量
-
$_REQUEST — HTTP Request 变量
默认情况下包含了 $_GET,$_POST 和 $_COOKIE 的数组。
注意:以命令行方式运行时,将不包含 argv 和 argc 信息;它们将存在于 $_SERVER 数组。
-
$_SESSION — Session 变量
-
$_ENV — 环境变量
-
$_COOKIE — HTTP Cookies
-
$http_response_header — HTTP 响应头
-
$argc — 传递给脚本的参数数目
-
$argv — 传递给脚本的参数数组
上下文
上下文主要用于文件系统或数据流封装协议。
上下文(Context)由 stream_context_create() 创建。选项可通过 stream_context_set_option() 设置,参数可通过 stream_context_set_params() 设置。
我们以HTTP协议上下文为例:
$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);
参考:PHP: HTTP context 选项 - Manual
除了HTTP上下文,还有套接字、FTP、SSL、CURL、Phar、MongoDB等上下文选项与参数设置,详见:PHP: 上下文(Context)选项和参数 - Manual
支持的协议和封装协议
用于描述一个封装协议的 URL 语法仅支持 scheme://… 的语法
-
file:// — 访问本地文件系统
-
http:// — 访问 HTTP(s) 网址
-
ftp:// — 访问 FTP(s) URLs
-
php:// — 访问各个输入/输出流(I/O streams)
重点关注php://input:
-
php://input
php://input是个可以访问请求的原始数据的只读流。 POST 请求的情况下,最好使用 php://input 来代替 $HTTP_RAW_POST_DATA,因为它不依赖于特定的 php.ini 指令。
注意:enctype="multipart/form-data” 的时候 php://input 是无效的。
-
php://output
php://output是一个只写的数据流, 允许以 print 和 echo 一样的方式 写入到输出缓冲区。
-
-
zlib:// — 压缩流
-
data:// — 数据(RFC 2397)
语法:
data://text/plain;base64,
// 打印 "I love PHP" echo file_get_contents('data://text/plain;base64,SSBsb3ZlIFBIUAo=');
经常会把小的图片通过data://协议直接将图片内容放到代码里,省去加载文件或请求链接的时间消耗。
-
glob:// — 查找匹配的文件路径模式
-
phar:// — PHP 归档
-
ssh2:// — Secure Shell 2
-
rar:// — RAR
-
ogg:// — 音频流
-
expect:// — 处理交互式的流