PHP面向对象中的重要知识点
Posted 脚本叔叔
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了PHP面向对象中的重要知识点相关的知识,希望对你有一定的参考价值。
转自:http://www.cnblogs.com/stephen-liu74/p/3500084.html
1. namespace:
和C++中的名字空间很像,作用也一样,都是为了避免在引用较多第三方库时而带来的名字冲突问题。通过名字空间,即便两个class的名称相同,但是因为位于不同的名字空间内,他们仍然可以被精确定位和区分。第一次看到php的名字空间语法时,感觉和C++相比在语法上是非常非常相似的,然而在写点儿小例子做做实验的时候才发现,他们的差别还是很大的,为了避免以后忘记,所以这里特别将其记录了下来。见如下代码:
<?php //in Test2.php namespace nstest\\test2; class Test2 public static function printMe() print 'This is nstest\\test2\\Test2::printSelf.'."\\n"; <?php //in Test1.php namespace nstest\\test1; class Test1 public static function printMe() print 'This is nstest\\test1\\Test1::printSelf.'."\\n"; require "Test2.php"; nstest\\test2\\Test2::printMe();
运行结果如下:
bogon:TestPhp$ php Test1.php PHP Fatal error: Class 'nstest\\test1\\nstest\\test2\\Test2' not found in /Users/liulei/PhpstormProjects/TestPhp/Test1.php on line 13
是不是这个结果比较出乎意料,原因在哪呢?HOHO,原来PHP在进行名字空间引用的时候,如果名字空间的第一个字符不是前导斜杠(\\),那么就被自动识别为相对名字空间,在上面的代码中,Test1自身所在的名字空间是namespace nstest\\test1,因此在以nstest\\test2\\Test2::printMe()方式调用Test2::printMe()时,PHP将自动解析为nstest\\test1\\nstest\\test2\\Test2::printMe(),即认为nstest\\test2是在当前名字空间内部的。修正该问题非常简单,只需在引用时加上前导斜杠(\\)即可,见以下修复后的代码:
<?php //Test2.php namespace nstest\\test2; class Test2 public static function printMe() print 'This is nstest\\test2\\Test2::printSelf.'."\\n"; <?php //Test1.php namespace nstest\\test1; class Test1 public static function printMe() print 'This is nstest\\test1\\Test1::printSelf.'."\\n"; require "Test2.php"; \\nstest\\test2\\Test2::printMe();
运行结果如下:
bogon:TestPhp$ php Test1.php
This is nstest\\test2\\Test2::printSelf.
还有一种改动方式,可以示意一下PHP中名字空间中的相对引用。这里我们可以将Test1的名字空间改为namespace nstest,其他的修改见以下代码中红色高亮部分:
<?php //Test2.php namespace nstest\\test2; class Test2 public static function printMe() print 'This is nstest\\test2\\Test2::printSelf.'."\\n"; <?php //Test1.php namespace nstest; class Test1 public static function printMe() print 'This is nstest\\test1\\Test1::printSelf.'."\\n"; require "Test2.php"; test2\\Test2::printMe();
运行结果等于上面正确的结果。最重要的差别就是该例使用了PHP名字空间中的相对定位。相信熟悉C++的开发者一定会想到use关键字,PHP也提供了该关键字,他们的功能是一致的,都是为了避免在后面的代码中,无需再通过全限定符(类名前加名字空间前缀)来引用其他名字空间中的类了。至于具体的语法规则,还是看看下面具体的代码和关键性注释吧。
<?php //Test2.php namespace nstest\\test2; class Test2 public static function printMe() print 'This is nstest\\test2\\Test2::printSelf.'."\\n"; <?php //Test1.php namespace nstest\\test1; class Test1 public static function printMe() print 'This is nstest\\test1\\Test1::printSelf.'."\\n"; require "Test2.php"; //这里需要特别注意的是,nstest\\test2已经表示名字空间绝对路径定位,不需要再加前导斜杠(\\)了。 //另外这里还有一个隐式规则是test2表示该名字空间的缺省别名,在引用其名字空间内的对象时需要加test2前缀。 use nstest\\test2; test2\\Test2::printMe(); //这里我们也可以给名字空间显式的指定别名,如: use nstest\\test2 as test2_alias; test2_alias\\Test2::printMe();
运行结果如下:
bogon:TestPhp$ php Test1.php
This is nstest\\test2\\Test2::printSelf.
This is nstest\\test2\\Test2::printSelf.
最后介绍一下PHP中全局名字空间的引用方式,见如下代码和关键性注释:
<?php class Test public static function printMe() print 'This is Global namespace Test::printSelf.'."\\n"; //下面两行代码表示的是同一对象,即全局名字空间下的Test类,然而如果因为名字空间冲突导致第一种方式不能被PHP //编译器正常识别,那么就可以使用第二种方式显式的通知PHP,自己要引用的是全局名字空间中的Test类。 Test::printMe(); \\Test::printMe();
运行结果如下:
bogon:TestPhp$ php Test1.php
This is Global namespace Test::printSelf.
This is Global namespace Test::printSelf.
2. Reflection:
PHP中的反射和Java中java.lang.reflect包提供的功能一样,更有意思的是,就连很多方法命名和调用方式也是非常雷同的。他们都是由一些列可以分析类、类方法和方法参数的PHP内置类组成。我们这里主要介绍的是如下几个常用的内置类:(Reflection、RelectionClass、ReflectionMethod、ReflectionParameter和ReflectionProperty)。现在我们还是一步一步来理解,即从ReflectionClass开始给出示例代码和关键性注释:
<?php class TestClass public $publicVariable; function publicMethod() print "This is publicMethod.\\n"; function classInfo(ReflectionClass $c) $details = ""; //getName将返回实际的类名。 $name = $c->getName(); if ($c->isUserDefined()) $details .= "$name is user defined.\\n"; if ($c->isInternal()) $details .= "$name is built-in.\\n"; if ($c->isAbstract()) $details .= "$name is abstract class.\\n"; if ($c->isFinal()) $details .= "$name is final class.\\n"; if ($c->isInstantiable()) $details .= "$name can be instantiated.\\n"; else $details .= "$name cannot be instantiated.\\n"; return $details; function classSource(ReflectionClass $c) $path = $c->getFileName(); $lines = @file($path); //获取类定义代码的起始行和截至行。 $from = $c->getStartLine(); $to = $c->getEndLine(); $len = $to - $from + 1; return implode(array_slice($lines,$from - 1,$len)); print "The following is Class Information.\\n"; print classInfo(new ReflectionClass('TestClass')); print "\\nThe following is Class Source.\\n"; print classSource(new ReflectionClass('TestClass'));
运行结果如下:
bogon:TestPhp$ php reflection_test.php The following is Class Information. TestClass is user defined. TestClass can be instantiated. The following is Class Source. class TestClass public $publicVariable; function publicMethod() print "This is publicMethod.\\n";
下面让我们仍然以代码示例和关键性注释的方法继续ReflectionMethod的学习之旅。
<?php class TestClass public $publicVariable; function __construct() private function privateMethod() function publicMethod() print "This is publicMethod.\\n"; function publicMethod2(string $arg1, int $arg2) //这个函数中使用的ReflectionMethod中的方法都是非常简单直观的,就不再过多赘述了。 function methodInfo(ReflectionMethod $m) $name = $m->getName(); $details = ""; if ($m->isUserDefined()) $details .= "$name is user defined.\\n"; if ($m->isInternal()) $details .= "$name is built-in.\\n"; if ($m->isAbstract()) $details .= "$name is abstract.\\n"; if ($m->isPublic()) $details .= "$name is public.\\n"; if ($m->isProtected()) $details .= "$name is protected.\\n"; if ($m->isPrivate()) $details .= "$name is private.\\n"; if ($m->isStatic()) $details .= "$name is static.\\n"; if ($m->isFinal()) $details .= "$name is final.\\n"; if ($m->isConstructor()) $details .= "$name is constructor.\\n"; if ($m->returnsReference()) $details .= "$name returns a reference.\\n"; return $details; function methodSource(ReflectionMethod $m) $path = $m->getFileName(); $lines = @file($path); $from = $m->getStartLine(); $to = $m->getEndLine(); $len = $to - $from + 1; return implode(array_slice($lines, $from - 1, $len)); $rc = new ReflectionClass('TestClass'); $methods = $rc->getMethods(); print "The following is method information.\\n"; foreach ($methods as $method) print methodInfo($method); print "\\n--------------------\\n"; print "The following is Method[TestClass::publicMethod] source.\\n"; print methodSource($rc->getMethod('publicMethod'));
运行结果如下:
bogon:TestPhp$ php reflection_test.php The following is method information. __construct is user defined. __construct is public. __construct is constructor. -------------------- privateMethod is user defined. privateMethod is private. -------------------- publicMethod is user defined. publicMethod is public. -------------------- publicMethod2 is user defined. publicMethod2 is public. -------------------- The following is Method[TestClass::publicMethod] source. function publicMethod() print "This is publicMethod.\\n";
让我们继续ReflectionParameter吧,他表示的是成员函数的参数信息。继续看代码吧。
<?php class ParamClass class TestClass function publicMethod() print "This is publicMethod.\\n"; function publicMethod2(ParamClass $arg1, &$arg2, $arg3 = null) function paramInfo(ReflectionParameter $p) $details = ""; //这里的$declaringClass将等于TestClass。 $declaringClass = $p->getDeclaringClass(); $name = $p->getName(); $class = $p->getClass(); $position = $p->getPosition(); $details .= "\\$$name has position $position.\\n"; if (!empty($class)) $classname = $class->getName(); $details .= "\\$$name must be a $classname object\\n"; if ($p->isPassedByReference()) $details .= "\\$$name is passed by reference.\\n"; if ($p->isDefaultValueAvailable()) $def = $p->getDefaultValue(); $details .= "\\$$name has default: $def\\n"; return $details; $rc = new ReflectionClass('TestClass'); $method = $rc->getMethod('publicMethod2'); $params = $method->getParameters(); foreach ($params as $p) print paramInfo($p)."\\n";
运行结果如下:
bogon:TestPhp$ php reflection_test.php $arg1 has position 0. $arg1 must be a ParamClass object $arg2 has position 1. $arg2 is passed by reference. $arg3 has position 2. $arg3 has default:
上面介绍的都是通过PHP提供的Reflection API来遍历任意class的具体信息,事实上和Java等其他语言提供的反射功能一样,PHP也同样支持通过反射类调用实际对象的方法,这里将主要应用到两个方法,分别是ReflectionClass::newInstance()来创建对象实例,另一个是ReflectionMethod::invoke(),根据对象实例和方法名执行该方法。见如下代码:
<?php class TestClass private $privateArg; function __construct($arg) $this->privateArg = $arg; function publicMethod() print '$privateArg = '.$this->privateArg."\\n"; function publicMethod2($arg1, $arg2) print '$arg1 = '.$arg1.' $arg2 = '.$arg2."\\n"; $rc = new ReflectionClass('TestClass'); $testObj = $rc->newInstanceArgs(array('This is private argument.')); $method = $rc->getMethod('publicMethod'); $method->invoke($testObj); $method2 = $rc->getMethod('publicMethod2'); $method2->invoke($testObj,"hello","world");
运行结果如下:
bogon:TestPhp$ php reflection_test.php $privateArg = This is private argument. $arg1 = hello $arg2 = world
事实上ReflectionClass、ReflectionMethod和ReflectionParameter提供给我们的可用方法还有更多,这里只是给出几个最典型的方法,以便我们可以更为直观的学习和了解PHP Reflection API。相信再看完以后的代码示例之后,我们都会比较清楚,如果今后需要用到和class相关的功能,就从ReflectionClass中查找,而member function的信息则一定来自于ReflectionMethod,方法参数信息来自于ReflectionParameter
以上是关于PHP面向对象中的重要知识点的主要内容,如果未能解决你的问题,请参考以下文章