1. namespace:

和C++中的名字空间很像,作用也一样,都是为了避免在引用较多第三方库时而带来的名字冲突问题。通过名字空间,即便两个class的名称相同,但是因为位于不同的名字空间内,他们仍然可以被精确定位和区分。第一次看到PHP的名字空间语法时,感觉和C++相比在语法上是非常非常相似的,然而在写点儿小例子做做实验的时候才发现,他们的差别还是很大的,为了避免以后忘记,所以这里特别将其记录了下来。见如下代码:

  1. <?php
  2. //in Test2.php
  3. namespace nstest\test2;
  4.  
  5. class Test2 {
  6. public static function printMe() {
  7. print 'This is nstest\test2\Test2::printSelf.'."\n";
  8. }
  9. }
  10.  
  11. <?php
  12. //in Test1.php
  13. namespace nstest\test1;
  14.  
  15. class Test1 {
  16. public static function printMe() {
  17. print 'This is nstest\test1\Test1::printSelf.'."\n";
  18. }
  19. }
  20. require "Test2.php";
  21. nstest\test2\Test2::printMe();

运行结果如下:

  1. bogon:TestPhp$ php Test1.php
  2. PHP Fatal error: Class 'nstest\test1\nstest\test2\Test2' not found in /Users/liulei/PhpstormProjects/TestPhp/Test1.php on line

是不是这个结果比较出乎意料,原因在哪呢?HOHO,原来PHP在进行名字空间引用的时候,如果名字空间的第一个字符不是前导斜杠(\),那么就被自动识别为相对名字空间,在上面的代码中,Test1自身所在的名字空间是namespace nstest\test1,因此在以nstest\test2\Test2::printMe()方式调用Test2::printMe()时,PHP将自动解析为nstest\test1\nstest\test2\Test2::printMe(),即认为nstest\test2是在当前名字空间内部的。修正该问题非常简单,只需在引用时加上前导斜杠(\)即可,见以下修复后的代码:

  1. <?php
  2. //Test2.php
  3. namespace nstest\test2;
  4.  
  5. class Test2 {
  6. public static function printMe() {
  7. print 'This is nstest\test2\Test2::printSelf.'."\n";
  8. }
  9. }
  10.  
  11. <?php
  12. //Test1.php
  13. namespace nstest\test1;
  14.  
  15. class Test1 {
  16. public static function printMe() {
  17. print 'This is nstest\test1\Test1::printSelf.'."\n";
  18. }
  19. }
  20. require "Test2.php";
  21. \nstest\test2\Test2::printMe();

运行结果如下:

  1. bogon:TestPhp$ php Test1.php
  2. This is nstest\test2\Test2::printSelf.

还有一种改动方式,可以示意一下PHP中名字空间中的相对引用。这里我们可以将Test1的名字空间改为namespace nstest,其他的修改见以下代码中红色高亮部分:

  1. <?php
  2. //Test2.php
  3. namespace nstest\test2;
  4.  
  5. class Test2 {
  6. public static function printMe() {
  7. print 'This is nstest\test2\Test2::printSelf.'."\n";
  8. }
  9. }
  10.  
  11. <?php
  12. //Test1.php
  13. namespace nstest;
  14. class Test1 {
  15. public static function printMe() {
  16. print 'This is nstest\test1\Test1::printSelf.'."\n";
  17. }
  18. }
  19.  
  20. require "Test2.php";
  21. test2\Test2::printMe();

运行结果等于上面正确的结果。最重要的差别就是该例使用了PHP名字空间中的相对定位。相信熟悉C++的开发者一定会想到use关键字,PHP也提供了该关键字,他们的功能是一致的,都是为了避免在后面的代码中,无需再通过全限定符(类名前加名字空间前缀)来引用其他名字空间中的类了。至于具体的语法规则,还是看看下面具体的代码和关键性注释吧。

  1. <?php
  2. //Test2.php
  3. namespace nstest\test2;
  4.  
  5. class Test2 {
  6. public static function printMe() {
  7. print 'This is nstest\test2\Test2::printSelf.'."\n";
  8. }
  9. }
  10.  
  11. <?php
  12. //Test1.php
  13. namespace nstest\test1;
  14.  
  15. class Test1 {
  16. public static function printMe() {
  17. print 'This is nstest\test1\Test1::printSelf.'."\n";
  18. }
  19. }
  20.  
  21. require "Test2.php";
  22. //这里需要特别注意的是,nstest\test2已经表示名字空间绝对路径定位,不需要再加前导斜杠(\)了。
  23. //另外这里还有一个隐式规则是test2表示该名字空间的缺省别名,在引用其名字空间内的对象时需要加test2前缀。
  24. use nstest\test2;
  25. test2\Test2::printMe();
  26.  
  27. //这里我们也可以给名字空间显式的指定别名,如:
  28. use nstest\test2 as test2_alias;
  29. test2_alias\Test2::printMe(); 

运行结果如下:

  1. bogon:TestPhp$ php Test1.php
  2. This is nstest\test2\Test2::printSelf.
  3. This is nstest\test2\Test2::printSelf.

最后介绍一下PHP中全局名字空间的引用方式,见如下代码和关键性注释:

  1. <?php
  2. class Test {
  3. public static function printMe() {
  4. print 'This is Global namespace Test::printSelf.'."\n";
  5. }
  6. }
  7.  
  8. //下面两行代码表示的是同一对象,即全局名字空间下的Test类,然而如果因为名字空间冲突导致第一种方式不能被PHP
  9. //编译器正常识别,那么就可以使用第二种方式显式的通知PHP,自己要引用的是全局名字空间中的Test类。
  10. Test::printMe();
  11. \Test::printMe();

运行结果如下:

  1. bogon:TestPhp$ php Test1.php
  2. This is Global namespace Test::printSelf.
  3. This is Global namespace Test::printSelf.

2. Reflection:

PHP中的反射和Java中java.lang.reflect包提供的功能一样,更有意思的是,就连很多方法命名和调用方式也是非常雷同的。他们都是由一些列可以分析类、类方法和方法参数的PHP内置类组成。我们这里主要介绍的是如下几个常用的内置类:(Reflection、RelectionClass、ReflectionMethod、ReflectionParameter和ReflectionProperty)。现在我们还是一步一步来理解,即从ReflectionClass开始给出示例代码和关键性注释:

  1. <?php
  2. class TestClass {
  3. public $publicVariable;
  4.  
  5. function publicMethod() {
  6. print "This is publicMethod.\n";
  7. }
  8. }
  9.  
  10. function classInfo(ReflectionClass $c) {
  11. $details = "";
  12. //getName将返回实际的类名。
  13. $name = $c->getName();
  14. if ($c->isUserDefined()) {
  15. $details .= "$name is user defined.\n";
  16. }
  17. if ($c->isInternal()) {
  18. $details .= "$name is built-in.\n";
  19. }
  20. if ($c->isAbstract()) {
  21. $details .= "$name is abstract class.\n";
  22. }
  23. if ($c->isFinal()) {
  24. $details .= "$name is final class.\n";
  25. }
  26. if ($c->isInstantiable()) {
  27. $details .= "$name can be instantiated.\n";
  28. } else {
  29. $details .= "$name cannot be instantiated.\n";
  30. }
  31. return $details;
  32. }
  33.  
  34. function classSource(ReflectionClass $c) {
  35. $path = $c->getFileName();
  36. $lines = @file($path);
  37. //获取类定义代码的起始行和截至行。
  38. $from = $c->getStartLine();
  39. $to = $c->getEndLine();
  40. $len = $to - $from + 1;
  41. return implode(array_slice($lines,$from - 1,$len));
  42. }
  43.  
  44. print "The following is Class Information.\n";
  45. print classInfo(new ReflectionClass('TestClass'));
  46.  
  47. print "\nThe following is Class Source.\n";
  48. print classSource(new ReflectionClass('TestClass'));

运行结果如下:

  1. bogon:TestPhp$ php reflection_test.php
  2. The following is Class Information.
  3. TestClass is user defined.
  4. TestClass can be instantiated.
  5.  
  6. The following is Class Source.
  7. class TestClass {
  8. public $publicVariable;
  9.  
  10. function publicMethod() {
  11. print "This is publicMethod.\n";
  12. }
  13. }

下面让我们仍然以代码示例和关键性注释的方法继续ReflectionMethod的学习之旅。

  1. <?php
  2. class TestClass {
  3. public $publicVariable;
  4.  
  5. function __construct() {
  6.  
  7. }
  8. private function privateMethod() {
  9.  
  10. }
  11. function publicMethod() {
  12. print "This is publicMethod.\n";
  13. }
  14. function publicMethod2(string $arg1, int $arg2) {
  15.  
  16. }
  17. }
  18.  
  19. //这个函数中使用的ReflectionMethod中的方法都是非常简单直观的,就不再过多赘述了。
  20. function methodInfo(ReflectionMethod $m) {
  21. $name = $m->getName();
  22. $details = "";
  23. if ($m->isUserDefined()) {
  24. $details .= "$name is user defined.\n";
  25. }
  26. if ($m->isInternal()) {
  27. $details .= "$name is built-in.\n";
  28. }
  29. if ($m->isAbstract()) {
  30. $details .= "$name is abstract.\n";
  31. }
  32. if ($m->isPublic()) {
  33. $details .= "$name is public.\n";
  34. }
  35. if ($m->isProtected()) {
  36. $details .= "$name is protected.\n";
  37. }
  38. if ($m->isPrivate()) {
  39. $details .= "$name is private.\n";
  40. }
  41. if ($m->isStatic()) {
  42. $details .= "$name is static.\n";
  43. }
  44. if ($m->isFinal()) {
  45. $details .= "$name is final.\n";
  46. }
  47. if ($m->isConstructor()) {
  48. $details .= "$name is constructor.\n";
  49. }
  50. if ($m->returnsReference()) {
  51. $details .= "$name returns a reference.\n";
  52. }
  53. return $details;
  54. }
  55.  
  56. function methodSource(ReflectionMethod $m) {
  57. $path = $m->getFileName();
  58. $lines = @file($path);
  59. $from = $m->getStartLine();
  60. $to = $m->getEndLine();
  61. $len = $to - $from + 1;
  62. return implode(array_slice($lines, $from - 1, $len));
  63. }
  64.  
  65. $rc = new ReflectionClass('TestClass');
  66. $methods = $rc->getMethods();
  67. print "The following is method information.\n";
  68. foreach ($methods as $method) {
  69. print methodInfo($method);
  70. print "\n--------------------\n";
  71. }
  72.  
  73. print "The following is Method[TestClass::publicMethod] source.\n";
  74. print methodSource($rc->getMethod('publicMethod'));

运行结果如下:

  1. bogon:TestPhp$ php reflection_test.php
  2. The following is method information.
  3. __construct is user defined.
  4. __construct is public.
  5. __construct is constructor.
  6.  
  7. --------------------
  8. privateMethod is user defined.
  9. privateMethod is private.
  10.  
  11. --------------------
  12. publicMethod is user defined.
  13. publicMethod is public.
  14.  
  15. --------------------
  16. publicMethod2 is user defined.
  17. publicMethod2 is public.
  18.  
  19. --------------------
  20. The following is Method[TestClass::publicMethod] source.
  21. function publicMethod() {
  22. print "This is publicMethod.\n";
  23. }

让我们继续ReflectionParameter吧,他表示的是成员函数的参数信息。继续看代码吧。

  1. <?php
  2. class ParamClass {
  3.  
  4. }
  5.  
  6. class TestClass {
  7. function publicMethod() {
  8. print "This is publicMethod.\n";
  9. }
  10. function publicMethod2(ParamClass $arg1, &$arg2, $arg3 = null) {
  11.  
  12. }
  13. }
  14.  
  15. function paramInfo(ReflectionParameter $p) {
  16. $details = "";
  17. //这里的$declaringClass将等于TestClass。
  18. $declaringClass = $p->getDeclaringClass();
  19. $name = $p->getName();
  20. $class = $p->getClass();
  21. $position = $p->getPosition();
  22. $details .= "\$$name has position $position.\n";
  23. if (!empty($class)) {
  24. $classname = $class->getName();
  25. $details .= "\$$name must be a $classname object\n";
  26. }
  27. if ($p->isPassedByReference()) {
  28. $details .= "\$$name is passed by reference.\n";
  29. }
  30. if ($p->isDefaultValueAvailable()) {
  31. $def = $p->getDefaultValue();
  32. $details .= "\$$name has default: $def\n";
  33. }
  34. return $details;
  35. }
  36.  
  37. $rc = new ReflectionClass('TestClass');
  38. $method = $rc->getMethod('publicMethod2');
  39. $params = $method->getParameters();
  40.  
  41. foreach ($params as $p) {
  42. print paramInfo($p)."\n";
  43. }

运行结果如下:

  1. bogon:TestPhp$ php reflection_test.php
  2. $arg1 has position .
  3. $arg1 must be a ParamClass object
  4.  
  5. $arg2 has position .
  6. $arg2 is passed by reference.
  7.  
  8. $arg3 has position .
  9. $arg3 has default:

上面介绍的都是通过PHP提供的Reflection API来遍历任意class的具体信息,事实上和Java等其他语言提供的反射功能一样,PHP也同样支持通过反射类调用实际对象的方法,这里将主要应用到两个方法,分别是ReflectionClass::newInstance()来创建对象实例,另一个是ReflectionMethod::invoke(),根据对象实例和方法名执行该方法。见如下代码:

  1. <?php
  2. class TestClass {
  3. private $privateArg;
  4. function __construct($arg) {
  5. $this->privateArg = $arg;
  6. }
  7. function publicMethod() {
  8. print '$privateArg = '.$this->privateArg."\n";
  9. }
  10.  
  11. function publicMethod2($arg1, $arg2) {
  12. print '$arg1 = '.$arg1.' $arg2 = '.$arg2."\n";
  13. }
  14. }
  15.  
  16. $rc = new ReflectionClass('TestClass');
  17. $testObj = $rc->newInstanceArgs(array('This is private argument.'));
  18. $method = $rc->getMethod('publicMethod');
  19. $method->invoke($testObj);
  20.  
  21. $method2 = $rc->getMethod('publicMethod2');
  22. $method2->invoke($testObj,"hello","world");

运行结果如下:

  1. bogon:TestPhp$ php reflection_test.php
  2. $privateArg = This is private argument.
  3. $arg1 = hello $arg2 = world

事实上ReflectionClass、ReflectionMethod和ReflectionParameter提供给我们的可用方法还有更多,这里只是给出几个最典型的方法,以便我们可以更为直观的学习和了解PHP Reflection API。相信再看完以后的代码示例之后,我们都会比较清楚,如果今后需要用到和class相关的功能,就从ReflectionClass中查找,而member function的信息则一定来自于ReflectionMethod,方法参数信息来自于ReflectionParameter。

注:该Blog中记录的知识点,是在我学习PHP的过程中,遇到的一些PHP和其他面向对象语言相比比较独特的地方,或者是对我本人而言确实需要簿记下来以备后查的知识点。虽然谈不上什么深度,但是还是希望能与大家分享。

PHP面向对象中的重要知识点(三)的更多相关文章

  1. PHP面向对象中的重要知识点(二)

    1. __toString: 当对象被打印时,如果该类定义了该方法,则打印该方法的返回值,否则将按照PHP的缺省行为输出打印结果.该方法类似于Java中的toString(). <?php cl ...

  2. PHP面向对象中的重要知识点(一)

    1. __construct: 内置构造函数,在对象被创建时自动调用.见如下代码: <?php class ConstructTest { private $arg1; private $arg ...

  3. 第35节:Java面向对象中的多线程

    Java面向对象中的多线程 多线程 在Java面向对象中的多线程中,要理解多线程的知识点,首先要掌握什么是进程,什么是线程?为什么有多线程呢?多线程存在的意义有什么什么呢?线程的创建方式又有哪些?以及 ...

  4. [.net 面向对象程序设计进阶] (4) 正则表达式 (三) 表达式助手

    [.net 面向对象程序设计进阶] (2) 正则表达式(三) 表达式助手 上面两节对正则表达式的使用及.NET下使用正则表达式作了详细说明,本节主要搜集整理了常用的正则表达式提供参考. 此外为了使用方 ...

  5. 文成小盆友python-num8 面向对象中的成员,成员修饰符,特殊成员,异常处理,设计模式之单例模式

    本节主要内容: 1.面向对象中的成员 2.成员修饰符 3.特殊成员 4.异常处理 5.设计模式之单例模式 一.面向对象中的成员(类的成员) 类的成员总共可以分为3大类,每类中有不同的分支. 1.总述, ...

  6. JavaScript面向对象中的继承

    1.1继承的基本概念 使用一个子类,继承另一个父类,那么子类可以自动拥有父类中的所有属性和方法,这个过程叫做继承. >>>继承的两方,发生在两个类之间. 实现继承的三种方式: 扩展O ...

  7. Python面向对象:杂七杂八的知识点

    为什么有这篇"杂项"文章 实在是因为python中对象方面的内容太多.太乱.太杂,在写相关文章时比我所学过的几种语言都更让人"糟心",很多内容似独立内容.又似相 ...

  8. python基础知识点三

    内置函数和匿名函数 python 一共有68个内置的函数:它们就是python提供给你直接可以拿来使用的所有函数 内置函数的图:链接 :https://www.processon.com/mindma ...

  9. 探讨 JS 的面向对象中继承的那些事

    最近学了 JS 的面向对象,这篇文章主要是探讨 JS 的面向对象中继承的那些事. JS中继承的特点: 1.子类继承父类: 2.子类可以用父类的方法和属性 3.子类的改变可以不影响父类 下面用一个例子来 ...

随机推荐

  1. xml笔记

    <?xml version="1.0" encoding="UTF-8"?> ----------------------------------- ...

  2. DataTable汇总

    一.排序 1 获取DataTable的默认视图 2 对视图设置排序表达式 3 用排序后的视图导出的新DataTable替换就DataTable (Asc升序可省略,多列排序用"," ...

  3. GEF: 图形拖拽处理

    重写EditPart#getDragTracker 即可替换拖拽事件.

  4. WebAPI的一种单元测试方案

    大家是如何对webApi写测试的呢? 1.利用Fiddler直接做请求,观察response的内容. 2.利用Httpclient做请求,断言response的内容. 3.直接调用webApi的act ...

  5. (文摘)彻底理解webservice SOAP WSDL

    WebServices特点介绍 WebServices 提供一个建立分布式应用的平台,使得运行在不同操作系统和不同设备上的软件,或者是用不同的程序语言和不同厂商的软件开发工具开发的软件,所有可能的已开 ...

  6. 区分各浏览器的CSS hack(包括360、搜狗、opera)

    虽然说使用css hack来解决页面兼容性bug并不是个好办法,但是有时候这些hack还是用的着的,比如你接受了一个二手或是三手的遗留界面,杂乱无章的css代码,只在某个浏览器下有兼容bug,而且需要 ...

  7. redis使用心得

    原创文章转载请注明出处:@协思, http://zeeman.cnblogs.com   redis是继memcached之后兴起的内存数据库,作者非常崇尚简洁高效,力求以最简单的方式最高效的解决问题 ...

  8. BIT祝威博客汇总(Blog Index)

    +BIT祝威+悄悄在此留下版了个权的信息说: 关于硬件(Hardware) <穿越计算机的迷雾>笔记 继电器是如何成为CPU的(1) 继电器是如何成为CPU的(2) 关于操作系统(Oper ...

  9. 细说.NET中的多线程 (三 使用Task)

    上一节我们介绍了线程池相关的概念以及用法.我们可以发现ThreadPool. QueueUserWorkItem是一种起了线程之后就不管了的做法.但是实际应用过程,我们往往会有更多的需求,比如如果更简 ...

  10. 可拖动的DIV续

    之前写过一篇可拖动的DIV讲如何实现可拖动的元素,最后提出了几点不足,这篇文章主要就是回答着三个问题 1. 浏览器兼容性 2. 边界检查 3. 拖动卡顿.失灵 先附上上次代码 <!DOCTYPE ...