PHP5.0还没有命名空间的概念,所以通常类文件的加载都是通过require,require_once,include,include_once. include()和require()语句的不同在于它们如何处理错误。使用require()调用文件发生错误时,将会停止整个程序;调用Include()时遇到相同的错误,则会生成警告并停止执行包含文件,跳出调用代码然后继续执行。
require()和require_once用于包含库文件时更加安全,而include()和include_once()则适用于加载模板这样的操作。必须了解的一点是require和require_once是语句而不函数。出于安全的考虑,应尽可能使用require & require_once
常常要加载一些基本的类文件,如果通过using命名空间来导入有时候也会过于繁杂,当然可以写一些函数来包括某些类文件,PHP5还提供了自动加载这一功能,只要实现function __autoload($classname)即可。
动态引用类
PHP可以动态地引用类,这一功能类似于反射,所谓的动态地引用类,只不过是给一个类的字符串,同样也可实例化对象,如下代码所示:
//TestC.php
class TestC {
public function sayHello()
{
return 'hello';
}
}
//index.php
require_once './TestC.php';
$classname = "TestC";
$pro = new $classname();
print $pro->sayHello();
如果想让系统能够运行用户创建的插件,这个功能非常有用。在开发实际项目时,需要先检查类是否存在、它是否拥有将要使用的方法等,以避免可能存在的风险。虽然PHP5有功能更强大的反射API,然而这些类函数更加接地气,首选原因大概是它能够与PHP4兼容。
查找类
class_exists()函数接受表示类的字符串,检查并返回布尔值。检查一个类是否存在。代码如下:
$classname = "TestC";
$path = "./".$classname.'.php';
if(!file_exists($path))
{
throw new Exception('No such file');
}
require_once $path;
if(!class_exists($classname))
{
throw new Exception('No such class');
}
get_declared_classes()函数来获得脚本进程中定义的所有类的数组。
get_class()函数用于获取对象的类名字符串。
对于判断一个对象是否某家族的一个对象,instanceof可以解决。
get_class_methods()获取类的方法数组
is_callable()函数它接受字符串变量形式的方法名作为第一个参数,如果类方法存在且可被调用,则返回true.
也可以传递一个数组参数,数组第一个参数是类名或对象,第二个参数是方法。
method_exists()函数检查一个类或者对象是否存在某一方法,它比is_callable低级一些。
get_class_vars()函数返回一个类或者对象的属性数组,属性名为键名,属性值作为键值。
get_parent_class()可以获得一个类的父类,如果不存在这样的类,则返回false.
is_subclass_of()函数检测类是否是另一个类的派生类,它需要一个子类对象和父类的名字。如果第二个参数是第一个参数的父类的话,该函数就返回true. 一个类是否实现了某个接口不能使用这个函数来确定。
call_user_func()可以调用方法或函数,要调用一个函数,需要将字符串作为它的第一个参数。要调用类方法,则需要一个数组,第一个参数是对象,第二个是调用的方法名。
call_user_func_array()函数可能更好用,因为它可以传递实实在在的参数,PHP中有__call拦截器,也可以调用函数,但参数只能当成一个数组传过去,而这个函数则可以还原成对应的参数列表。
反射
PHP对于反射实现了一些类,具体与下图所示:
简单的查看一个类的所有信息代码如下:
$classinfo = new ReflectionClass($classname);
Reflection::export($classinfo);
ReflectionClass接受唯一的参数:类名。Relection工具类输出类的相关信息。它有一个静态方法export(),用于格式化和输出这个类的管理的数据。
可以使用反射类中的函数来查看关于的类的所有信息,类声明如下:类详情
ReflectionClass implements Reflector {
/* 常量 */
const integer IS_IMPLICIT_ABSTRACT = 16 ;
const integer IS_EXPLICIT_ABSTRACT = 32 ;
const integer IS_FINAL = 64 ;
/* 属性 */
public $name ;
/* 方法 */
public __construct ( mixed $argument )
public static string export ( mixed $argument [, bool $return = false ] )
public mixed getConstant ( string $name )
public array getConstants ( void )
public ReflectionMethod getConstructor ( void )
public array getDefaultProperties ( void )
public string getDocComment ( void )
public int getEndLine ( void )
public ReflectionExtension getExtension ( void )
public string getExtensionName ( void )
public string getFileName ( void )
public array getInterfaceNames ( void )
public array getInterfaces ( void )
public ReflectionMethod getMethod ( string $name )
public array getMethods ([ int $filter ] )
public int getModifiers ( void )
public string getName ( void )
public string getNamespaceName ( void )
public object getParentClass ( void )
public array getProperties ([ int $filter ] )
public ReflectionProperty getProperty ( string $name )
public string getShortName ( void )
public int getStartLine ( void )
public array getStaticProperties ( void )
public mixed getStaticPropertyValue ( string $name [, mixed &$def_value ] )
public array getTraitAliases ( void )
public array getTraitNames ( void )
public array getTraits ( void )
public bool hasConstant ( string $name )
public bool hasMethod ( string $name )
public bool hasProperty ( string $name )
public bool implementsInterface ( string $interface )
public bool inNamespace ( void )
public bool isAbstract ( void )
public bool isCloneable ( void )
public bool isFinal ( void )
public bool isInstance ( object $object )
public bool isInstantiable ( void )
public bool isInterface ( void )
public bool isInternal ( void )
public bool isIterateable ( void )
public bool isSubclassOf ( string $class )
public bool isTrait ( void )
public bool isUserDefined ( void )
public object newInstance ( mixed $args [, mixed $... ] )
public object newInstanceArgs ([ array $args ] )
public object newInstanceWithoutConstructor ( void )
public void setStaticPropertyValue ( string $name , string $value )
public string __toString ( void )
}
通过反射我们可以很容易实现查看类的源代码,一个简单的工具类如下:
class ReflectionUtil
{
static function getClassSource(ReflectionClass $class)
{
$path = $class->getFileName();
$lines = @file($path); //这里并未处理异常
$from = $class->getStartLine();
$to = $class->getEndLine();
$len = $to-$from+1;
return implode(array_slice($lines, $from-1,$len));
}
}
当然,似乎这种方法只能显示自定义的类,也许可以改进使之可以查看更多的类的源码。
也可以很容易查看类的方法和属性,这些可以使用另外两个类ReflectionMethod及ReflectionProperty,它们可以通过ReflectionClass类的方法getMethod(getMethods)及getProperty(getProperties)
来获得.具体如何获得相关信息,可以查看这两个类的实现。ReflectionProperty | ReflectionMethod
其它一些反射类可以查看PHP手册。
下面一个摘自一本书的实例,和大家分享一下,反射除了获取类的基本信息,还有一个重要的方法:newInstance
它可以创建类的实例。
实例概要:从配置文件中读取要实例化类的相关信息,然后实例化类,执行。也就是说这个类可以加载第三方插件。为所有的插件实现一个Module接口,它包含方法execute,有一个ModuleRunner,负责依据配置文件实例化类。所的实现了Module接口的类中的设置方法都是setProperty的形式,这样的做的原因是要能够使ModuleRunner能够识别这些方法并调用。具体代码如下:
<?php
//TestC.php
class Person{
public $name;
public function __construct($name)
{
$this->name = $name;
}
}
interface Module{
public function execute();
}
class PersonModule implements Module
{
public function setPerson(Person $person)
{
print 'PersonModule:setPerson...this person name is '.$person->name.'<br />';
}
public function execute()
{
print "PersonModule:execute".'<br />';
}
}
class FtpModule implements Module
{
public function setHost($host)
{
print 'host :'.$host.'<br />';
}
public function setUser($user)
{
print 'user :'.$user.'<br />';
}
public function execute()
{
print 'FtpModule:execute'.'<br />';
}
}
//index.php
<?php
require_once './TestC.php';
class ModuleRunner
{
//由配置文件得到的配置信息,这里直接给出
private $config = array(
'PersonModule' => array('person' => 'yourname'),
'FtpModule' => array('host' => 'http://www.baidu.com',
'user' => 'username' ),
);
private $moduleArr = array();
public function init()
{
$interface = new ReflectionClass('Module');
foreach ($this->config as $modulename => $param)
{
$module_class = new ReflectionClass($modulename);
if(! $module_class->isSubclassOf($interface))
{
throw new Exception('unknown module type: '.$modulename);
}
$module = $module_class->newInstance();
//对模块类的设置方法进行执行
foreach ($module_class->getMethods() as $method)
{
$this->handleMethod($module,$method,$param);
}
array_push($this->moduleArr, $module);
}
}
private function handleMethod(Module $module,ReflectionMethod $method,$param)
{
$methodname = $method->getName();
$args = $method->getParameters();
//不是set方法,而且这里只要求一个参数
if(count($args)!= 1 || substr($methodname, 0,3) != 'set'){
return false;
}
//设置参数不在时
$property = strtolower(substr($methodname, 3));
if(!isset($param[$property])){
return false;
}
//确定参数的类型并执行之
$arg_class = $args[0]->getClass();
if(empty($arg_class)){//基本类型
$method->invoke($module,$param[$property]);
}
else
{
//参数类型是对象,
$method->invoke($module,$arg_class->newInstance($param[$property]));
}
}
public function execute()
{
foreach ($this->moduleArr as $module)
$module->execute();
}
}
$test = new ModuleRunner();
$test->init();
$test->execute();
?>