函数处理函数
一、参数获取:必须在函数中调用才有意义
* 1.get_num_args(void):返回参数数量
* 2.get_get_args():返回参数值索引数组
* 3.get_get_arg(int):返回指定索引的参数值
<?php function demo1($a,$b,$c) { //返回实际参数组成的索引数组 // return func_get_args();//无参数,[0]=>php,[1]=>java,[2]=>c // return func_get_arg(1); //参数为实参索引,获取第2个参数值:java return func_num_args();//无参数,返回参数数量:3 } print_r(demo1('php','java','c'));
二、2个执行回调的函数
* 我们调用php系统提供的函数叫直接调用,也叫: 直调
* php系统调用用户自定义的函数,必须要通过一个代理函数来调用,叫间接调用,也叫回调
* 我们今天学习的call_user_func()和call_user_func_array()就是这样的代理函数
* 可以代替系统来调用我们自定义的函数
1.call_user_func($callback, $array):第一个参数作为回调函数调用
<? php //1.参数逐个传入使用:call_user_func() //单个参数 echo call_user_func(function(){ //如果匿名回调没写参数,可以通过func_get_args()获取 return '欢迎来到'.func_get_arg(0); },'php中文网'); echo '<hr>'; echo call_user_func(function($site){ return '欢迎来到'.$site; },'php中文网'); echo '<hr>'; //多个参数,实参数量与回调参数数量必须一致,多余会被忽略 echo call_user_func(function($site,$course){ return '欢迎来到'.$site.'学习'.$course.'课程'; },'php中文网','php'); echo '<hr>'; //支持调用类中的方法做为回调 class Demo1 { //普通方法 public function sum($a, $b) { return $a+$b; } } class Demo2 { //静态方法 public static function multi($a,$b) { return $a*$b; } } //调用普通方法:回调必须以数组方式传入: [对象,'方法名'] $obj = new Demo1; //echo call_user_func([$obj,'sum'],10,15); //php5.4+以上,可以简写: echo call_user_func([(new Demo1()),'sum'],10,15); echo '<hr>'; //调用静态方法:有字符串和数组二种方式 //字符串: '类名::方法名' echo call_user_func('Demo2::multi',10,15); //数组: [类名,'方法名'] echo '<hr>'; echo call_user_func(['Demo2','multi'],20,5); //如果有命名空间,只要在类名前加上命名空间字符串就可以了,或者用常量__NAMESPACE__
2.call_user_func_array($callback, $array):使用数组参数执行回调函数
* 2.call_user_func_array(callback, array)
* 只接受二个参数:回调,索引数组,传给回调的参数全部打包到索引数组中
* 与call_user_func(array,arg1...)不同之处仅仅传递参数方式不同
* 回调同样支持:字符串函数名,类名,数组,匿名函数
<? php echo '<hr>'; echo call_user_func_array(function(){ //查看传递给回调的参数 // return print_r(func_get_args(),true); $msg = '前端开发的基础知识包括:'; //遍历参数数组 foreach (func_get_args() as $value) { $msg .= $value.','; } //去掉未尾逗号,并替换成感叹号 return rtrim($msg,',').'!'; }, ['html','css','javascript']); echo '<hr>'; class Demo3 { public function func1() { return __METHOD__.'<pre>'.print_r(func_get_args(),true).'</pre>'; } } class Demo4 { public static function func2() { return __METHOD__.'<pre>'.print_r(func_get_args(),true).'</pre>'; } } //回调类中的方法 echo call_user_func_array([(new Demo3),'func1'], ['php','java','c']); //调用静态方法的回调: //1.使用数组参数方式 echo call_user_func_array(['Demo4','func2'], ['html','css','jQuery']); //2.使用字符串参数方式 echo call_user_func_array('Demo4::func2', ['html','css','jQuery']); //如果有命名空间,只需要在类名前加上命名空间或__NAMESPACE__即可
* 注意: 1.回调中通常不需要写参数,而是通过其它函数来获取;2.参数不允许使用引用传递
eval()与create_function()
一、eval()
* 1.eval()函数把字符串按照 PHP 代码来计算
* 2.该字符串必须是合法的 PHP 代码,且必须以分号结尾
* 3.如果没有在代码字符串中调用 return 语句,则返回 NULL
* 4.如果代码中存在解析错误,则 eval() 函数返回 false
* 5.该函数对于在数据库文本字段中供日后计算而进行的代码存储很有用
二、create_function('参数','函数体代码'):创建匿名函数
实例
<? php //以下二条语句功能完全一样 eval('echo 4+5;'); //输出9 echo eval('return 4+5;'); //返回9并显示在屏幕上 //尽管上面二条语句功能一样,但返回值不相同 //所以,如果想要引用eval()返回值,必须在语句字符串中使用return var_dump(eval('echo 4+5;')); //返回 NULL var_dump(eval('return 4+5;')); //返回 9 //eval()注入攻击演示 isset($_GET['p']) ? eval($_GET['p']) : null; //现在在url后面添加 ?p=phpinfo(); 或其它php合法语句,会直接执行,注入成功 //你可以加入你的广告,你的跳转地址等,达到恶意攻击的目的 //用create_functoin()创建匿名函数 //因为该函数已被弃用,部分编辑器会给出警告,多说无益 //知道这个函数曾经来过这个世界上就足够了 $func1 = create_function('$a,$b', 'return ($a+$b);'); echo $func1(10,20);
运行实例 »
点击 "运行实例" 按钮查看在线实例
范围解析符::
一、用途
* 1.调用静态成员(类外部)
* 2.调用类常量
* 3.调用被子类覆盖的类成员(属性/方法)
二、场景
* 1.类外部:前面仅允许通过类名来访问类中成员
* 2.类内部:前面仅允许: self, parent,static
三、特殊: 允许使用类名变量来引用类名(php5.3+)
实例
<?php class Demo1 { const HOME = '合肥'; public static function test1() { return __METHOD__; } public static function test2() { //延迟静态绑定:后面会说到 //static::根据上下文来决定使用哪个类来调用 return static::test1(); } } class Demo2 extends Demo1 { public static $name = '朱老师'; public static function test1() { //调用本类静态成员: self:: $res = self::$name.'<br>'; //调用父类静态成员属性: parent:: $res .= parent::HOME.'<br>'; //调用父类静态成员方法 $res .= parent::test1().'<br>'; $res .= __METHOD__; return $res; } } //1.类外部调用类静态成员 echo Demo1::HOME,'<br>'; echo Demo1::test1(); echo '<hr>'; echo Demo2::test1(); echo '<hr>'; //注意Demo1调用test2()和Demo2调用test2()的区别 //Demo1::test()返回Demo1中的test1()运行结果 echo Demo1::test2(); echo '<hr>'; //Demo2::test2()返回Demo2中的test1()运行结果 echo Demo2::test2(); echo '<hr>'; //php5.3+,允许使用类变量来调用 $class = 'Demo2'; echo $class::test2();
运行实例 »
点击 "运行实例" 按钮查看在线实例
细说static
一、用途:
* 1.类中声明静态成员
* 2.函数中声明静态变量
* 3.后期静态绑定
二、特点:
* 1.静态成员属于类,必须用类访问
* 2.静态方法比较特殊,允许用对象访问,但不推荐
* 3.必须使用范围解析符::访问,不允许使用对象访问符->访问
* 4.静态属性仅允许使用字面量(字符/数值/数组)或常量进行初始化(禁止表达式/变量/对象)
三、后期静态绑定/延迟静态绑定
* 注:php.net官网上有介绍非常的晦涩难懂,这里做一个简化说明
* 1. 转发调用是什么?
* 答:说人话,就是以回调的方式来执行静态方法,用回调就必须指定调用者或代理函数。
* 调用者可以是self,parent,static
* 代理函数有很多:
* forward_static_call(callable,arg1...)
* forward_static_call_array(callable,array)
* call_user_func(callable,arg1...)
* call_user_func_array(callable,array)
<?php //forward_static_call()在类中调用本类或其它类的静态方法,类似call_user_func() class Demo1 { public static function test1() { //返回参数:func_get_args(void )返回参数数组 return implode(',', func_get_args()); } //后期静态绑定(最后再创建这个父类方法用来教学) public static function test3() { //调用当前类的静态方法 // return self::test1('php','mysql','thinkphp'); //使用static,动态判断是哪个类在调用test1()方法 //类方法的调用者,在声明时指定,是前期调用,在编译阶段完成 //类方法的调用者,在执行时指定,是后期调用,在运行阶段完成 return static::test1('php','mysql','thinkphp'); } }
class Demo2 extends Demo1 { //重写父类静态方法 public static function test1() { return __METHOD__; } public static function test2() { //1.传统调用父类静态方法 return parent::test1('php中文网'); //2.用代理函数调用父类静态方法 //return forward_static_call(['Demo1','test1'],'Hello','PHP中文网'); //下call_user_func()功能完全一样 //return call_user_func(['Demo1','test1'],'Hello','PHP中文网'); //调用本类的静态方法 //return forward_static_call(['Demo2','test1']); //如果第一个参数是一个字符串,是指一个全局函数,不属于任何类 //return forward_static_call('test3','www.php.cn'); } }
//创建一个外部函数 function test3() { return '我是类外部定义的一个函数,'. func_get_arg(0); } //echo Demo2::test2();
//后期静态绑定
//用Demo2类静态调用test3(),而test3()中调用了本类的test1()
echo Demo2::test3();
/**
* 分析
* 1. test3()方法在Demo1类中
* 2. Demo2类继承自Demo1,所以可以用Demo2访问test3()
* 3. test3()中访问了当前类的test1()方法,当然是Demo1类的test1()
* 4. 但是我是用Demo2类调用的test3(),因此我的本意,肯定是想调用Demo2中重载的test1()
* 5. 那么,如果能让当前静态方法的调用者,根据继承关系的上下文自动进行判断呢?
* 6. 在静态方法前使用static关键字,这样就可以自动判断是哪个类在调用我了~~
* 7. 运行时会自动将static,替换成Demo2类
*/
//思考:在Demo1中调用test3()会输出什么结果?
echo Demo1::test3();
* 1.会输出Demo1类中的test1()方法的运行结果
* 2.因为当前调用者是Demo1,运行时会自动将static替换成Demo1