导航

4、对象工具

4.1、PHP与包

PHP没有支持”包”机制,但我们应该将代码组织类似“包”的结构。

低版本的PHP5,我们可以通过包结构的文件系统来组织类。

require_once ‘util/xujiajun1.php’;

require_once ‘util2/xujiajun2.php’;

PHP5.3命名空间引入

example:

  1. //foo.php:
  2. <?php
  3. namespace foo;
  4. class xujiajun
  5. {
  6. static function says()
  7. {
  8. echo "hi,namespace";
  9. }
  10. }
  11. ?>
  12. //bar.php
  13. <?php
  14. namespace bar;
  15. require_once 'foo.php';
  16. class xujiajun
  17. {
  18. static function says()
  19. {
  20. echo "@hi,namespace2";
  21. }
  22. }
  23. echo \foo\xujiajun::says();
  24. echo \bar\xujiajun::says();
  25. //输出hi,namespace@hi,namespace2
  26. ?>
4.2、类函数和对象函数

PHP提供了一系列强大的函数来检测和对象

查找类
example:

  1. //class_exists()函数接受表示类的字符串,检查并返回布尔值。
  2. $className = "superu";
  3. if (!class_exists($className))
  4. {
  5. throw new Exception("No such class $className");
  6. }
  7. //当然,我们无法确定该类的构造方法是否需要参数。在这个级别的安全上,应该求助反射api

PHP有很多用于检测对象类型的基本工具。首先,可以使用get_class()函数检查对象的类,他接受任何对象作为参数。

example:

  1. $person = new Xujiajun();
  2. if(get_class($person) == "Xujiajun")
  3. {
  4. print "$person is a Xujiajun object\n";
  5. }

我们有时候只想确定一个类的类型,比如只想知道是不是Person这个类的,我们不关心是不是Xujiajun还是Superu。
为此,PHP提供了instanceof这个操作符。

example:

  1. $person = new Person();
  2. if($person instanceof Person)
  3. {
  4. echo "$person is a Person object\n";
  5. }

我们如果需要知道类中的方法,怎么办?PHP提供了get_class_methods()函数得到一个类中的所有方法列表。

example:

  1. <?php
  2. class Xujiajun
  3. {
  4. public function getAge()
  5. {
  6. # code...
  7. }
  8. public function getFullName()
  9. {
  10. # code...
  11. }
  12. }
  13. $p = new Xujiajun();
  14. var_dump(get_class_methods($p));
  15. //输出
  16. array(2) {
  17. [0] =>
  18. string(6) "getAge"
  19. [1] =>
  20. string(11) "getFullName"
  21. }

注意:只有声明成public的方法才会显示哦。

如果我们要检测某一个方法是否存在,用以上这个get_class_method当然可以做到:

  1. if( in_array($method,get_class_methods($p)))
  2. {
  3. }

其实PHP有更加专业的工具:method_existsis_callable,

example:

  1. //is_callable()
  2. if (is_callable(array($p, "getAge"))) {
  3. echo $p->getAge(); // 18
  4. }
  5. //method_exists()
  6. if (method_exists($p, "getAge")) {
  7. echo $p->getAge();// 18
  8. }

注意以上两者区别:如果getAge方法改成private、protected,is_callable返回是false,而method_exists返回true

了解类属性
我们既然可以查询类的方法,当然我们也可以查询类的属性。PHP提供了get_class_vars函数,参数接收类名。

example:

  1. class Xujiajun
  2. {
  3. public $age = "18";
  4. }
  5. var_dump(get_class_vars('Xujiajun'));
  6. //输出结果
  7. array(1) {
  8. 'age' =>
  9. string(2) "18"
  10. }

了解继承

类函数也允许我们绘制继承关系。我们可以用get_parent_class()来找到一个类的父类。参数为一个类名或者对象。

example:

  1. class Person
  2. {
  3. }
  4. class Xujiajun extends Person
  5. {
  6. }
  7. var_dump(get_parent_class(new Xujiajun));//string(6) "Person"

另外也可以用is_subclass_of来检测类是否是另一个类的派生类。它需要一个子类对象(或者类)和父类的名字。

example:

  1. var_dump(is_subclass_of('Xujiajun', 'Person'));//bool(true)

方法调用

比如现在我们要字符串动态调用某一个方法:

  1. $p = getPerson();//获取Person对象
  2. $method = "getAge";//定义方法名
  3. echo $p->$method();

其实,我们可以通过PHP提供的call_user_func()达到相同目的。

example:

  1. class Xujiajun
  2. {
  3. public function getAge($age = 0)
  4. {
  5. return 18;
  6. }
  7. }
  8. var_dump(call_user_func(array(new Xujiajun,'getAge')));//18
  9. //加参数
  10. var_dump(call_user_func(array(new Xujiajun,'getAge'),2));//20

当然 call_user_func_array() 更加好用,使用上相同,参数支持数组传参。

4.3、反射API
4.3.1、入门

PHP中的反射API就像Java中的java.lang.reflect包一样。它由一系列可以分析属性、方法和类内置类组成。

反射API部分列参考

类 |描述
————-|————————————————-
Relection|为类的摘要信息提供静态函数export()
RelectionClass|类信息和工具
RelectionMethod|类方法信息和工具
RelectionParameter|方法参数信息
RelectionProperty|类属性信息
RelectionFunction|函数信息和工具
RelectionExcetion|错误类
RelectionExtension|PHP扩展信息

利用这些反射API的类,可以运行访问对象、函数和脚本中的扩展信息。反射API非常强大,我们应该经常使用API而少使用类和对象函数。

example:

php class Person { public function getName() { } } class Xujiajun extends Person { public $age = "18"; public function getAge($arg = 0) { return 18+$arg; } } $reflector = new ReflectionClass('Xujiajun'); $properties = Reflection::export($reflector); var_dump($properties); //输出类似 Class [ <user> class Xujiajun extends Person ] { @@ /private/var/www/demo/foo.php 11-25 - Constants [0] { } - Static properties [0] { } - Static methods [0] { } - Properties [1] { Property [ <default> public $age ] } - Methods [2] { Method [ <user> public method getAge ] { @@ /private/var/www/demo/foo.php 16 - 19 - Parameters [1] { Parameter #0 [ <optional> $arg = 0 ] } } Method [ <user, inherits Person> public method getName ] { @@ /private/var/www/demo/foo.php 5 - 8 } } }
从例子中可以看出,Relection::export()提供了Xujiajun这个类的几乎所有信息。
如果我们直接var_dump()一个对象的话,前提要实例化它,而且也不会有细节提供。可见,反射API提供了更高层次的功能。

4.3.2、类检测

example:

  1. interface Person
  2. {
  3. public function getName();
  4. }
  5. $reflector = new ReflectionClass('Person');
  6. var_dump($reflector->isInterface());//bool(true)
4.3.3、检查方法

ReflectionClass我们用来检查类,那么检查类中的方法可以用RelectionMethod

获取RelectionMethod对象的方式有两种:

0、从RelectionClass::getMethods()获得RelectionMethod对象数组

1、如果是特定的类方法,RelectionClass::getMethod()接受一个方法名作为参数并返回相应的RelectionMethod对象。

example:

  1. class Xujiajun
  2. {
  3. public function getName()
  4. {
  5. }
  6. }
  7. $reflector = new ReflectionClass('Xujiajun');
  8. $methods = $reflector->getMethods();
  9. var_dump($methods[0]->isPublic());//bool(true)
4.3.4、检查方法参数

在PHP5中声明类方法时可以限制参数中对象类型,因此检查方法的参数变得非常必要。

example:

  1. class Person
  2. {
  3. public function __construct(Exception $a)
  4. {
  5. }
  6. public function getName()
  7. {
  8. }
  9. }
  10. $reflector = new ReflectionClass('Person');
  11. $methods = $reflector->getMethod('__construct');
  12. $params = $methods->getParameters();
  13. foreach ($params as $key => $param) {
  14. echo argData($param);//$a must be a Foo object
  15. }
  16. function argData(ReflectionParameter $arg)
  17. {
  18. $details = "";
  19. $name = $arg->getName();
  20. $class = $arg->getClass();
  21. if (!empty($class)) {
  22. $className = $class->getName();
  23. $details .= "\$$name must be a $className object\n";
  24. }
  25. return $details;
  26. }
4.3.5、使用反射API

example:

  1. class Person
  2. {
  3. public $name;
  4. public function __construct($name)
  5. {
  6. $this->name = $name;
  7. }
  8. }
  9. interface Module
  10. {
  11. }
  12. class FtpModule implements Module
  13. {
  14. function setHost($host)
  15. {
  16. echo "FtpModule:setHost: $host\n";
  17. }
  18. function setUser($user)
  19. {
  20. echo "FtpModule:setUser: $user\n";
  21. }
  22. function excute()
  23. {
  24. }
  25. }
  26. class PersonModule implements Module
  27. {
  28. function setPerson(Person $person)
  29. {
  30. echo "PersonModule::setPerson: {$person->name}\n";
  31. }
  32. function excute()
  33. {
  34. }
  35. }
  36. class ModuleRunner
  37. {
  38. private $configData = array(
  39. 'PersonModule' => array(
  40. 'person' => 'anon'
  41. ),
  42. 'FtpModule' => array(
  43. 'host' => 'superu.org',
  44. 'user' => 'xujiajun'
  45. )
  46. );
  47. public function init()
  48. {
  49. $interface = new ReflectionClass('Module');
  50. foreach ($this->configData as $moduleName => $params) {
  51. $moduleClass = new ReflectionClass($moduleName);
  52. if (!$moduleClass->isSubclassOf($interface)) {
  53. throw new Exception("unknow module type: $moduleClass");
  54. }
  55. $module = $moduleClass->newInstance();
  56. foreach ($moduleClass->getMethods() as $method) {
  57. $this->handleMethod($module,$method,$params);
  58. }
  59. }
  60. }
  61. private function handleMethod(Module $module, ReflectionMethod $method, $params)
  62. {
  63. $name = $method->getName();
  64. $args = $method->getParameters();
  65. if (substr($name, 0,3) != 'set' or count($args) != 1) {
  66. return false;
  67. }
  68. $property = strtolower(substr($name,3));
  69. if (!isset($params[$property])) {
  70. return false;
  71. }
  72. $argClass = $args[0]->getClass();
  73. if (empty($argClass)) {
  74. $method->invoke($module,$params[$property]);
  75. } else {
  76. $method->invoke($module,$argClass->newInstance($params[$property]));
  77. }
  78. }
  79. }
  80. $test = new ModuleRunner();
  81. $test->init();
  82. //输出:
  83. // PersonModule::setPerson: anon
  84. // FtpModule:setHost: superu.org
  85. // FtpModule:setUser: xujiajun