`

PHP函数式编程的特性研究

    博客分类:
 
阅读更多
PHP不是像Lisp那样的函数式编程语言,更多的,PHP适合用C的风格来编写代码 
PHP中没有“函数”这种类型,也就是说,函数不能直接用变量来传递 
比如下面的代码
Php代码function test() {   
1.       echo "Just A Test!!!";   
2.   }   
3.   $test1=test;   
4.   echo gettype($test1);//输出string  

PHP是这样解析上面的代码的
Php代码  
1.   //出现一个裸字符串,不以$符开头,那么就把它当成常量   
2.   $test1=test;//PHP将test当成一个常量,但代码中并没有test这一常量,接着PHP将常量名当成其值   
3.   //当PHP遇到一个未定义常量时,就将它当成一个字符串值,如下   
4.     
5.   var_dump(UNDEFINED_CONSTANT);//将输出string(18) "UNDEFINED_CONSTANT"  

这表明PHP中的函数名在非调用的情况下(后面没有括号)会被当成一个字符串。 
而正巧PHP中有通过一个字符串名称动态调用一个函数的方法:
Php代码  
1.   $phpInfo='phpinfo';   
2.   $phpInfo();   
3.   //还有另外一种方式   
4.   call_user_func('phpinfo');  


在C语言中可以通过指向函数的指针来将函数作为参数传递以实现高级的FP, 
而在PHP中,则是通过将函数名称作为字符串传递,通过$fname()来调用 
($fname是一个字符串类型的变量,其值为函数名),或者通过call_user_func来调用 

那么到这里时就会发现PHP中函数的实现所带来的不便,因为函数是通过 
字符串来传递和调用的,那么就要求不能有函数名称相同的两个不同函数的存在 
即,PHP中的所有的函数(这里所说的“函数”不包括方法)都是全局函数, 
并且不允许在运行时重定义,因为如果允许重复定义函数(比如像大家熟悉的JavaScript那样), 
下面的代码就会出错
Php代码  
1.   //第一次定义test1函数   
2.   function test1() {   
3.       return 123;   
4.   }   
5.     
6.   //将变量$testFunction赋值为test1的函数名称   
7.   //事实上这种写法较低效,这样写更好一些:$testFunction='test1';   
8.   $testFunction=test1;   
9.     
10. //.......若干行代码之后   
11. //重新定义了test1   
12. function test1() {   
13.     return 456;   
14. }   
15.   
16. //输出与期望值不一样了,因为变量里只是保存的函数名而已   
17. //而并不是真正的对函数的引用   
18. echo $testFunction();  
 
 
 
 


既然PHP中所有函数都是全局函数,那么函数声明就自然不能 
嵌套着写了(指Closure),在讨论PHP中的Closure与Lambda之前 
先讨论一下PHP中的方法(即已经绑定到指定对象或类上的函数) 
先创建一个简单的类,将其静态方法直接输出看看是什么内容
Php代码  
1.   class Demo {   
2.       public static function test() {   
3.           return 123;   
4.       }   
5.   }   
6.     
7.   var_dump(Demo::test);//到这一步出错了   
8.   //因为PHP在解析Demo::test时,会将其看成是对Demo类的静态属性的读取   
9.   //而这个静态属性又不以$开头,那么就将其看成静态常量(const)   
10. //不存在这个常量,就出错了  
 


那么如果要动态的调用一个类(或实例)的方法该怎么办呢? 
先上一个现实中的例子,一个非常简单的Controller(当然真正的控制器不能这样简单)
Php代码  
1.   class Controller {   
2.       public function login() {   
3.           echo "Login";   
4.       }   
5.       public function home() {   
6.           echo "Home";   
7.       }   
8.       public function logout() {   
9.           echo "Logout!!!";   
10.     }   
11. }   
12. $action=$_GET['action'];//要调用的方法名称在action参数里面   
13. //接着要动态调用指定的方法   
14. Controller::$action();//使用这种方法,这主要得益于PHP的“变量的变量”机制   
15. //当然,要先检查一下类有没有这个方法,可以用PHP的反射机制检测   
16. $reflectionClass=new ReflectionClass('Controller');   
17. $reflectionClass->hasMethod($action);//测试一个是否有对应的方法   
18. //以及测试一下方法是否是静态方法   
19. $reflectionMethod=$reflectionClass->getMethod($action);   
20. $reflectionMethod->isStatic();//是静态方法则返回true  
 

除了使用PHP的可变变量机制外,还可以使用之前的call_user_func函数
Php代码  
1.   //传一个数组作为第一个参数,数组中第一个元素为类名,第二个元素为静态方法名称   
2.   call_user_func(array('Controller',$action));   
3.   //如果要向方法或函数传递参数,可以将参数附在后面   
4.   function test($a,$b) {   
5.       echo $a+$b;   
6.   }   
7.   call_user_func('test',123,456);   
8.   //或者在传不定参数时,使用call_user_func_array函数   
9.   call_user_func_array('test',array(123,456));   
10.   
11. //在PHP 4.1之前存在call_user_method函数及   
12. //call_user_method_array专门用来调用对象的方法,但现在废弃不用了  
 
如果需要调用实例的方法的话,则为
Php代码  
1.   class Demo {   
2.       public function test() {   
3.           echo "Instance Method";   
4.       }   
5.   }   
6.   $d=new Demo();   
7.     
8.   $d->$action();   
9.   //或者以这种方式调用   
10. call_user_func(array($d,$action));   
11. //如果类Demo没有将构造函数声明为私有的话,还仍然可以无需实例就调用实例方法   
12. call_user_func(array('Demo',$action));  

从上面的代码可以看得出来,PHP中的类也是以字符串形式的名称传递的 
像下在的代码
Php代码  
1.   $class=Demo;   
2.   echo gettype($class);//string   
3.   //原因和函数一样,PHP将Demo当成一个普通的裸字符串了  

到了PHP 5.2.3时,PHP中调用类的静态方法还有另外一种语法
Php代码  
1.   //PHP 5.2.3   
2.   call_user_func('Demo::'.$action);  

到了PHP 5.3开始,PHP引入了命名空间的概念,因此上面的调用将变成
Php代码  
1.   call_user_func(__NAMESPACE__.'::Demo::'.$action);  

看到上面的代码,其实可以猜想,类的静态方法不就是加了一个命名空间前缀的函数嘛 
(当然执行上下文及作用域不一样了) 
----------------------华丽的分隔线------------------------------ 
PHP中的Lambda及Closure 
在PHP 5.3之前,PHP中实现Lambda的语法非常别扭 
要将函数中的代码作为字符串传递给create_function方法去构造
Php代码  
1.   $lambda=create_function('$a','return addslashes(trim($a));');   
2.   //相当于构造这样一个函数   
3.   function lambda1($a) {   
4.       return addslashes(trim($a));   
5.   }  

这种对Lambda的支持真是鸡肋得很,用起来反而降低了效率 
将代码写到字符串中很容易出错 
这种风格的lambda创建方式只适用于非常简短的函数表达式 
那再看看上面的$lambda变量中保存的什么
Php代码  
1.   var_dump($lambda);   
2.   //仍然是字符串类型,并且命名特点为"lambda_"加一个数字   
3.   //后面的数字为PHP系统全局计数器,保证运行不会重复  

事实上用create_function创建的仍然是全局函数,只是这个全局函数的名字不会被猜到而已 
可能我们会尝试创建这样命名的函数
Php代码  
1.   function lambda_1() {   
2.       retunr 'Lambda!!!';   
3.   }   
4.   $lambda=create_function('','return 123;');   
5.   //如果你的PHP是系统启动后第一次运行,那么$lambda里面的字符串看起来就和lambda_1一样   
6.   //但执行代码调仍然一点没有错误   
7.   echo $lambda();//返回123;   
8.   echo lambda_1();//返回'Lambda!!!'  

其实,create_function生成的函数名有些特殊,它是NULL字符加上"lambda_"再加个一个数字标识 
而NULL字符我们是没有办法在声明函数时使用这个字符的,因此上面的代码不会冲突 
可以用下面的代码测试了看下
Php代码  
1.   $lambda=create_function('','return 123;');   
2.   preg_match('|\d+|',$lambda,$matches);//匹配出lambda数字标识   
3.   //通lambda数字编号来调用些函数表达式   
4.   call_user_func("\x00lambda_{$matches[0]}");//在字符串前面加个NULL字符   
5.   //始终输出123  

使用create_function不但写起来别扭,而且create_function方法只能创建一般的临时函数而已 
并不能实现闭包(虽然如果只是读取些标量的话可以变通一下实现,如下)
Php代码  
1.   function outer() {   
2.       $a=123;   
3.       return create_function('$a='.$a,'return $a;');   
4.   }   
5.   $inner=outer();   
6.   echo $inner();   
7.   //PHP有个很不爽的地主就是不把函数调用当成表达式,这种代码会出错   
8.   //outer()();或getRow()[0];  

而到了PHP 5.3,PHP开始支持真正的闭包,而且lambda更好使了,接近于JavaScript
Php代码  
1.   #!/usr/bin/php-5.3.0/php   
2.     
3.     
4.   $data=range(0,10);   
5.   //更接近于JavaScript的lambda语法   
6.   $newData=array_map(function ($v) {   
7.       $a=range(0,$v);   
8.       return array_sum($a);   
9.   },$data);   
10.   
11. function outer() {   
12.     $a=123;   
13.     //语法上生疏些,需要将要在lambda中访问的闭包变量列在use列表中   
14.     return function () use($a) {//闭包   
15.         echo $a;   
16.     };   
17. }   
18. $inner=outer();//只是不爽的地方就是,仍然不能这样写outer()();   
19. $inner();   
20. print_r($newData);   
21.   
22. //PHP也提供了像Lisp中的map,reduce,walk方法用于函数式编程   
23. //下面的代码用于将$_REQUEST中的每一个值进行trim   
24. array_walk('trim',$_REQUEST);  
 
PHP闭包中变量引用的问题 
…… 
如下面回复所提到的,下面的代码工作起来和想像中有些差距
Php代码  
1.   function outer() {   
2.       $a=0;   
3.       return function () use($a) {   
4.           return $a++;   
5.       };   
6.   }   
7.   $inner=outer();   
8.   echo $inner();//outputs 0   
9.   echo $inner();//outputs 0   
10. echo $inner();//outputs 0  

因为PHP中的闭包,只是对原变量中值进行拷贝而已,也就是说,use中列出来的变量,在运行时 
都是闭包函数中的局部变量,而并不是函数外面的变量
Php代码  
1.   function outer() {   
2.       $a=0;   
3.       return function () use($a) {//大致相当于有这样一个步骤:lambda1::$a=outer::$a   
4.           return $a++;   
5.       };   
6.   }  

如果要达到想像中的状态的话,只要在变量前加个按引用传值标识就行了
Php代码  
1.   function outer() {   
2.       $a=0;   
3.       return function () use(&$a) {   
4.           return $a++;   
5.       };   
6.   }   
7.   $inner=outer();   
8.   echo $inner();//0   
9.   echo $inner();//1   
10. echo $inner();//2   
11. echo $inner();//3  
 
作者:不详 来源:网络
分享到:
评论

相关推荐

    基于PHP实现响应式科学院研究院网站.zip

    响应式科学院研究院网站是利用PHP编程语言开发的一种网站,它具备了现代网页设计的重要特性——响应式布局。这种布局方式使得网站能够在不同设备上(如桌面电脑、平板电脑和手机)自适应显示,提供良好的用户体验。...

    php网络编程从入门到精通.rar

    在PHP与MySQL的结合应用中,你可以掌握构建动态、交互式网站的关键技能。 首先,PHP(Hypertext Preprocessor)是一种广泛应用的开源脚本语言,尤其适合Web开发。它允许开发者在服务器端执行代码,生成HTML响应,...

    PHP网站编程技术-PHP综合实验-PHP网上书店源码

    6. **URL路由**:源码可能包含了URL路由机制,用于解析用户请求并调用相应的PHP函数或控制器。这有助于构建清晰的URL结构,提高用户体验。 7. **类和对象**:面向对象编程(OOP)是PHP的关键特性,源码中可能会定义...

    php5帮助文档&php4完全中文手册

    PHP4的特点包括预定义的全局变量、函数式编程风格、以及较弱的类型系统。手册涵盖了PHP4的所有核心函数、语法结构和配置选项,对初学者和有经验的开发者都十分有用。 此外,压缩包中的"Smarty"可能指的是Smarty模板...

    深入理解PHP 高级技巧、面向对象与核心技术(原书第3版)源码

    这些技巧可能包括但不限于错误处理、性能优化、函数式编程、魔术方法、异常处理、命名空间以及PHP的内置数据结构如数组、哈希表和对象。例如,通过深入理解错误报告和日志记录,开发者可以更好地调试代码;而函数式...

    《细说PHP》配套源码

    12. **函数式编程**:虽然PHP更倾向于面向对象,但也有丰富的函数式编程功能,如高阶函数、闭包、匿名函数等,源码可能包含这些概念的应用。 通过研究这些源码,读者不仅可以掌握PHP的基本语法和常用函数,还能了解...

    PHP实例开发源码—84PHP开源框架.zip

    它支持面向对象编程,函数式编程,以及传统的过程式编程,具有丰富的扩展库和广泛的应用场景,如内容管理系统(CMS)、电子商务平台、社交媒体应用等。 【压缩包子文件的文件名称列表】: 1. **使用须知.txt**:这...

    PHP5.3.2011中文帮助文档手册

    2. **闭包(Closures)**:也称为匿名函数,PHP5.3开始支持闭包,这使得函数可以作为值传递,增强了函数式编程的能力。闭包还可以绑定到特定的变量上下文,方便实现回调函数和延迟执行等功能。 3. **晚期静态绑定...

    php-5.4.30.tar.gz

    3. ** Closure(闭包)**:增强了匿名函数的实现,使其可以作为值传递,增强了函数式编程的能力。 4. ** Namespaces(命名空间)**:PHP 5.3引入,5.4进一步完善,提供了更好的代码组织和隔离,降低了全局命名冲突...

    商业源码-编程源码-PHP v4.0 网络开发技术.zip

    PHP v4.0是PHP发展史上的一个重要阶段,它在1999年发布,引入了许多新的特性和改进,包括性能提升、面向对象编程的支持以及更强大的函数库。这个版本使得PHP成为了当时Web开发的主流选择之一。 二、面向对象编程 ...

    php-5.4.16-src

    - 新增了匿名函数(Closure),增强了函数式编程的能力。 - 内置了JSON编码和解码支持,提高了处理JSON数据的效率。 - `__DIR__`魔术常量现在总是指向定义它的文件所在的目录,而不是包含它的文件。 2. **性能...

    php-5.4.45-master.zip

    Closure 是 PHP 的匿名函数,可以作为变量存储,增强了函数式编程的能力。这些特性在5.4.45版本中得到了完善,进一步提升了代码的可读性和可维护性。 在数据库支持方面,PHP 5.4.45提供了mysqli和PDO两种接口。...

    基于PHP的H5响应式门户网站源码 php版.zip

    【描述】"基于PHP的H5响应式门户网站源码 php版.zip" 描述中提到的“源码”意味着这是一个可编辑的项目,开发者可以深入研究代码,了解其工作原理,进行定制化开发或二次开发。PHP是一种广泛使用的开源服务器端脚本...

    PHP5.4源码包

    同时,闭包可以作为类的属性存储,增强了函数式编程的能力。 5. **__DIR__魔术常量** `__DIR__`魔术常量在PHP5.4中被引入,它始终指向包含该常量的文件的目录,无论是在类中还是函数中,这对于获取当前文件路径...

    PHP source

    PHP支持函数式编程和面向对象编程。函数是PHP中的基本代码单元,可以全局定义或作为类的方法。类则是对象的模板,定义了属性和方法。PHP5引入了面向对象特性,如类、对象、继承、封装和多态。 五、PHP扩展开发 ...

    PHP核心技术与最佳实践.pdf

    面向对象程序设计基于三大范式之一,其他两种是面向过程和函数式编程。面向对象的核心思想包括对象、封装、可重用性和可扩展性。面向对象是一种更高级、更抽象的思维方式,它建立在面向过程之上,是一种更深层次的...

    PHP桔子sd平台网站源码整站打包.rar

    PHP 支持多种数据库系统,其中包括 MySQL,这是一个关系型数据库管理系统,因其高效、灵活和开源的特性,在 web 开发中被广泛应用。 在这个源码包中,你将找到一系列的 PHP 文件,这些文件包含了网站的各个功能模块...

    基于PHP的校园信息管理系统的设计与实现

    【基于PHP的校园信息管理系统的设计与实现】是一个典型的IT毕业设计项目,主要涉及PHP编程语言在构建校园管理系统中的应用。PHP是一种广泛使用的开源脚本语言,尤其适合于Web开发,可以嵌入到HTML中,创建动态交互式...

    基于php的办公自动化系统的设计与实现

    而PHPStudy则是一个集成的开发环境,集成了PHP、Apache、MySQL等组件,为开发者提供了一站式的本地开发环境,简化了开发和调试过程。 【知识点详解】 1. **PHP基础**:PHP是一种解释型的脚本语言,常用于Web开发,...

    php-5.4.16.tar.gz

    2. MySQLi:增强版的MySQL扩展,支持面向对象和过程式两种编程风格,5.4版本可能有更多新特性和改进。 六、安全强化 1. 过滤输入和输出:加强了`filter_var()`函数,提供更强大的数据验证和过滤功能,以防止SQL注入...

Global site tag (gtag.js) - Google Analytics