面向对象并不是PHP的关键,但PHP确实能很好的支持面向对象编程,而且类与对象也成为了PHP5的核心组成部分。PHP的面向对象特性让构建大型的复制应用成为可能,有了类与对象,就自然产生了各种编程范式和层出不穷的编程框架。今天我们来研究PHP中的类和对象。
先来一段官方的代码说明类的构成
<?php class SimpleClass { // property declaration public $var = 'a default value'; // method declaration public function displayVar() { echo $this->var; } } ?>
定义类的关键字是 class,这与其他语言没什么区别。类可以有变量,有方法,使用 this 访问对象自己。类的静态方法中 this 指调用的对象本身。
类中可定义属性,属性作用域,作用域可用关键字 public、private、protected 修饰,具体意义与c++相同。
另外可以使用 ClassName::class 来取得类的类型。实际上这返回的是一个带命名空间的完整路径。
比如,一下代码的输出会是:NS\ClassName
注意:
不要将类的定义分割到不同的文件或块中
为了兼容PHP4,var定义的成员变量(属性)与public具有相同的访问权限(大多数面向对象语言默认的是private)
可以动态的为类增加属性,如 $cart->newProperty = 4, $cart为类型Cart的实例,给Cart增加了newProperty属性,但这破坏了面向对象的原则,不提倡
方法也可以接受访问修饰符,默认 public
PHP 5 新增了一个 final 关键字。如果父类中的方法被声明为 final,则子类无法覆盖该方法。如果一个类被声明为 final,则不能被继承。
抽象类
PHP 5 支持抽象类和抽象方法。定义为抽象的类不能被实例化。任何一个类,如果它里面至少有一个方法是被声明为抽象的,那么这个类就必须被声明为抽象的。被定义为抽象的方法只是声明了其调用方式(参数),不能定义其具体的功能实现。
继承一个抽象类的时候,子类必须定义父类中的所有抽象方法;另外,这些方法的访问控制必须和父类中一样(或者更为宽松)。例如某个抽象方法被声明为受保护的,那么子类中实现的方法就应该声明为受保护的或者公有的,而不能定义为私有的。此外方法的调用方式必须匹配,即类型和所需参数数量必须一致。例如,子类定义了一个可选参数,而父类抽象方法的声明里没有,则两者的声明并无冲突。 这也适用于 PHP 5.4 起的构造函数。在 PHP 5.4 之前的构造函数声明可以不一样的。
<?php abstract class AbstractClass { // 强制要求子类定义这些方法 abstract protected function getValue(); abstract protected function prefixValue($prefix); // 普通方法(非抽象方法) public function printOut() { print $this->getValue() . "\n"; } } class ConcreteClass1 extends AbstractClass { protected function getValue() { return "ConcreteClass1"; } public function prefixValue($prefix) { return "{$prefix}ConcreteClass1"; } } class ConcreteClass2 extends AbstractClass { public function getValue() { return "ConcreteClass2"; } public function prefixValue($prefix) { return "{$prefix}ConcreteClass2"; } } $class1 = new ConcreteClass1; $class1->printOut(); echo $class1->prefixValue('FOO_') ."\n"; $class2 = new ConcreteClass2; $class2->printOut(); echo $class2->prefixValue('FOO_') ."\n"; ?>
<?php
$instance = new SimpleClass();
// 也可以这样做:
$className = 'Foo';
$instance = new $className(); // Foo()
?>
使用 new 创建对象,使用 -> 访问对象的属性和方法。PHP5中类为引用传递。
尝试加载未定义的类php5中可以使用 autoload() 函数,它在试图使用尚未被定义的类时自动加载。一般用于动态加载未引用的文件。
<?php function __autoload($class_name) { require_once $class_name . '.php'; } $obj = new MyClass1(); $obj2 = new MyClass2(); ?>
如果每个类都分布于同名文件中,该函数可以使php自动的加载类对应的文件。
spl_autoload_register() 提供了一种更加灵活的方式来实现类的自动加载。因此,不再建议使用 __autoload() 函数,在以后的版本中它可能被弃用。
<?php // function __autoload($class) { // include 'classes/' . $class . '.class.php'; // } function my_autoloader($class) { include 'classes/' . $class . '.class.php'; } spl_autoload_register('my_autoloader'); // 或者,自 PHP 5.3.0 起可以使用一个匿名函数 spl_autoload_register(function ($class) { include 'classes/' . $class . '.class.php'; }); ?>
构造函数
void __construct ([ mixed
$args
[,$...
]] )
具有构造函数的类会在每次创建新对象时先调用此方法。但要注意的是,在子类的显式构造函数中默认不会调用父类的构造函数。要执行父类的构造函数,需要在子类的构造函数中调用 parent::__construct()。子类没有构造函数,则默认调用父类的构造函数。
旧式的构造函数是一个与类名相同的函数,建议在代码用不使用。自 PHP 5.3.3 起,在命名空间中,与类名同名的方法不再作为构造函数。这一改变不影响不在命名空间中的类。
与其它方法不同,当 __construct() 被与父类 __construct() 具有不同参数的方法覆盖时,PHP 不会产生一个
E_STRICT
错误信息。
析构函数
void __destruct ( void )
PHP 5 引入了析构函数的概念,这类似于其它面向对象的语言,如 C++。析构函数会在到某个对象的所有引用都被删除或者当对象被显式销毁时执行。
和构造函数一样,父类的析构函数不会被引擎暗中调用。要执行父类的析构函数,必须在子类的析构函数体中显式调用 parent::__destruct()。此外也和构造函数一样,子类如果自己没有定义析构函数则会继承父类的。
试图在析构函数(在脚本终止时被调用)中抛出一个异常会导致致命错误。
在析构函数中调用 exit() 将会中止其余关闭操作的运行。
一个类可以在声明中用 extends 关键字继承另一个类的方法和属性。PHP不支持多重继承,一个类只能继承一个基类。
被继承的方法和属性可以通过用同样的名字重新声明被覆盖。但是如果父类定义方法时使用了 final,则该方法不可被覆盖。
可以通过 parent:: 来访问被覆盖的方法或属性。
当覆盖方法时,参数必须保持一致否则 PHP 将发出 E_STRICT 级别的错误信息。但构造函数例外,构造函数可在被覆盖时使用不同的参数。
<?php class BaseClass { function __construct() { print "In BaseClass constructor\n"; } } class SubClass extends BaseClass { function __construct() { parent::__construct(); print "In SubClass constructor\n"; } } class OtherSubClass extends BaseClass { // inherits BaseClass's constructor } // In BaseClass constructor $obj = new BaseClass(); // In BaseClass constructor // In SubClass constructor $obj = new SubClass(); // In BaseClass constructor $obj = new OtherSubClass(); ?>
除非使用了自动加载,否则一个类必须在使用之前被定义。如果一个类扩展了另一个,则父类必须在子类之前被声明。此规则适用于类继承其它类与接口。
类似于java, php使用 interface 定义接口,使用 implements 实现接口。接口可以继承多个接口。
<?php interface a { public function foo(); } interface b { public function bar(); } interface c extends a, b { public function baz(); } class d implements c { public function foo() { } public function bar() { } public function baz() { } } ?>
接口加上类型约束,提供了一种很好的方式来确保某个对象包含有某些方法。
instanceof 用于确定一个 PHP 变量是否属于某一类 class 的实例,
从php5.3,被废弃的is_a()又可以用了
<?php class ParentClass { } class MyClass extends ParentClass { } $a = new MyClass; var_dump($a instanceof MyClass); var_dump($a instanceof ParentClass); ?>
输出:
bool(true)
bool(true)
php具有完整的面向对象特性,类,继承,接口给了我们充分的发挥空间,利用这些特性,我们可以轻松的完成各种任务挑战。