基本概念
属性
类常量
对象继承
自动加载对象
构造和析构函数
访问控制
范围解析操作符(::)
Static 关键字
抽象类
接口
基本概念
定义
- 每个类的定义都以关键字 class 开头,后面跟着类名(非保留字)。
- 类名后跟着一对花括号,里面包含有类属性和方法的定义。
<?php
class A
{
//属性
public $a;
private $b;
//方法
public function actionA()
{
}
}
类成员默认值
在定义类属性的时候,可以使用默认值
<?php
class A
{
//默认值
public $a = 'Hi';
private $b = 'Hello';
//方法
public function actionA()
{
}
}
创建实例
要创建一个对象的实例,使用关键字 new
<?php
$a = new A();//创建类 A 的实例
对象赋值
当把一个对象已经创建的实例赋给一个新变量时,新变量会访问同一个实例,就和用该对象赋值一样。此行为和给函数传递入实例时一样。可以用克隆给一个已创建的对象建立一个新实例。
<?php
class A
{
}
$a = new A();
$b = $a;
$c = &$a;
$d = clone $a;
$a = null;
var_dump($a,$b,$c,$d);
上述结果为
NULL
object(A)#1 (0) {
}
NULL
object(A)#2 (0) {
}
$this
伪变量
t
h
i
s
可
以
在
当
一
个
方
法
在
对
象
内
部
调
用
时
使
用
。
this 可以在当一个方法在对象内部调用时使用。
this可以在当一个方法在对象内部调用时使用。this 是一个到调用对象(通常是方法所属于的对象,但也可以是另一个对象,如果该方法是从第二个对象内静态调用的话)的引用。
编辑 /home/project/this.php
<?php
class A
{
function actionA()
{
if (isset($this)) {
echo '$this is defined (';
echo get_class($this);
echo ")\n";
} else {
echo '$this is not defined.'.PHP_EOL;
}
}
}
class B
{
function actionB()
{
A::actionA();
}
}
$a = new A();
$a->actionA();
A::actionA();
$b = new B();
$b->actionB();
B::actionB();
执行
php this.php
从结果可以看出,$this 只能在对象中使用,不能在静态方法中调用。但是如果在另一个对象(类 B)中调用静态方法,则 $this 指向该类( B )。
对象继承
一个类可以在声明中用 extends 关键字继承另一个类的方法和成员。PHP 不支持多重继承,一个类只能继承一个类。
class A
{
}
class B extends A
{
}
被继承的方法和成员可以通过用同样的名字重新声明被覆盖,除非父类定义方法时使用了 final 关键字。可以通过 parent:: 来访问被覆盖的方法或成员。
编辑 /home/project/extends.php
<?php
class A
{
public function sayHi()
{
echo "Hi".PHP_EOL;
}
final public function sayBye()
{
echo "Bye".PHP_EOL;
}
}
class B extends A
{
public function sayHi()
{
parent::sayHi();
echo "Hello".PHP_EOL;
parent::sayBye();
}
//不能被覆盖,报错,练习的时候注意删除该方法
public function sayBye()
{
echo "See you";
}
}
$b = new B();
$b->sayHi();
执行
php extends.php
从结果可以看到
- 使用 final 修饰的方法不能被覆盖
- 使用 parent:: 可以调用父类方法或属性
属性
定义
类的变量成员叫做属性。属性声明是由访问控制关键字 public,protected 或 private 和一个变量来组成,同时可以加上默认值。
<?php
class A
{
//只能在类本身使用
private $a = "Hello";
//可以在子类和类本身使用
protected $b = <<<EOT
This is variable b;
EOT;
//除了子类,类本身,外部也可以访问
public $c;
}
访问属性
在类的成员方法里面,可以通过 $this-> 加变量名来访问类的属性和方法,但是要访问类的静态属性或者在静态方法要使用 self:: 加变量名。
注意 self:: 这种方式后的变量名需要加 $ 符号,而 $this-> 后的变量名不需要加
编辑 /home/project/property.php
<?php
class A
{
private $a = "Hello";
protected $b = <<<EOT
This is property b
EOT;
public static $c = 'This is a'.' static property';
public function talk()
{
echo $this->a.PHP_EOL;
echo $this->b.PHP_EOL;
echo self::$c;
}
}
(new A())->talk();
执行
php property.php
类常量
我们可以在类中定义常量。常量的值将始终保持不变。在定义和使用常量的时候不需要使用 $ 符号。
<?php
class A
{
const ENV = 'env';
const HELLO = 'Hello';
}
接口(interface)中也可以定义常量
<?php
interface B
{
const ENV = 'ENV';
public function sayHi();
}
自动加载对象
要进行一个类操作时,需要先将该类加载进来,例如 include,require 等。
如果要执行的类很多,则需要大量 include 操作,会导致重复加载,管理苦难等一系列问题。
在 PHP 5 中,不用这样做了,可以使用 spl_autoload_register() 函数来注册任意数量的自动加载器。
<?php
spl_autoload_register(function ($class_name) {
include $class_name . '.php';
});
new A();
new B();
本例尝试分别从 A.php 和 B.php 文件中加载 A 和 B 类,相当于
<?php
include 'A.php';
include 'B.php';
new A();
new B();
构造和析构函数
构造函数
void __constuct()
创建一个对象时( new 操作),构造函数会自动调用
class A
{
public function __construct()
{
echo 'init...'.PHP_EOL;
}
public function sayHi()
{
echo "hi";
}
}
(new A())->sayHi();
输出结果为
init…
hi
实例化 A 的时候执行构造函数。
注意: 如果子类中定义了构造函数则不会隐式调用其父类的构造函数。要执行父类的构造函数,需要在子类的构造函数中调用 parent::__construct()。
<?php
class A
{
public function __construct()
{
echo "A";
}
}
class B extends A
{
public function __construct()
{
parent::__construct();
echo "B";
}
}
new A(); // A
new B(); // AB
析构函数
void __destruct(void)
析构函数会在到某个对象的所有引用都被删除或者当对象被显式销毁时执行。
<?php
class A {
public function __construct() {
echo 'Start...';
}
public function sayHi()
{
echo "Hi...";
}
public function __destruct() {
echo "Finish";
}
}
(new A())->sayHi(); // Start...Hi...Finish
和构造函数一样,父类的析构函数不会被引擎暗中调用。要执行父类的析构函数,必须在子类的析构函数体中显式调用 parent::__destruct()。
析构函数即使在使用 exit() 终止脚本运行时也会被调用。在析构函数中 调用 exit() 将会中止其余关闭操作的运行。
访问控制
对属性或方法的访问控制,是通过在前面添加关键字 public
、protected
或 private
来实现的。如果未添加,则默认为 public
。
public
所定义的类成员可以在任何地方被访问
protected
所定义的类成员则可以被其所在类的子类和父类访问(当然,该成员所在的类也可以访问)
private
定义的类成员则只能被其所在类访问
编辑 /home/project/access.php
<?php
class A
{
private $hi = 'Hi'.PHP_EOL;
protected $hello = 'Hello'.PHP_EOL;
public $bye = 'Bye'.PHP_EOL;
private function sayHi()
{
echo $this->hi;
}
protected function sayHello()
{
echo $this->hello;
}
public function sayBye()
{
echo $this->bye;
}
}
class B extends A
{
public function talk()
{
parent::sayHello();
}
}
$a = new A();
$a->sayHi();//报错,无法调用
$b = new B();
$b->sayHello();//报错,无法调用
$b->talk();
$b->sayBye();
执行
php access.php
从结果可知,声明为 private 的方法或属性无法在类外部调用,同时子类也无法调用该方法。
范围解析操作符(::)
范围解析操作符,可以简单地说是一对冒号,可以用于访问静态成员、方法和常量,还可以用于覆盖类中的成员和方法。
当在类的外部访问这些静态成员、方法和常量时,必须使用类的名字。
编辑 /home/project/paamayim.php
<?php
class A
{
const CONST_A = 'A constant value';
public static function sayHello()
{
echo 'Hello';
}
}
class B extends A
{
public static $b = 'static var b';
/**
* 覆盖父类方法
*
*/
public static function sayHello()
{
echo parent::sayHello().' World'.PHP_EOL;
}
public static function actionB()
{
self::sayHello();
echo parent::CONST_A.PHP_EOL;
echo self::$b;
}
}
B::actionB();
执行
php paamayim.php
从结果可知
- 使用
parent,self
可以调用父类和自身的方法属性 ::
可以调用静态方法,静态属性和常量
Static 关键字
声明类成员或方法为 static,就可以不实例化类而直接访问。不能通过一个对象来访问其中的静态成员(静态方法除外)。
由于静态方法不需要通过对象即可调用,所以伪变量 $this 在静态方法中不可用。静态属性不可以由对象通过 -> 操作符来访问。
注意:在 PHP7 中通过(::)调用非静态方法会产生一个 E_DEPRECATED 级别的警告,不赞成这样使用,在以后可能会取消对这种用法的支持。
编辑 /home/project/static.php
<?php
class Test
{
public $hi = 'Hi';
public static $hello = 'Hello';
public function sayHi()
{
echo $this->hi;
}
public static function sayHello()
{
echo self::$hello;
}
public function sayWorld()
{
echo " World".PHP_EOL;
}
}
$obj = new Test();
$obj->sayHi();
$obj->sayWorld();
Test::sayHello();
Test::sayWorld();
执行
php static.php
从结果可以看出
- 通过 :: 可以执行静态和非静态方法,但是不赞成通过这种方式调用非静态方法,此方式有可能被官方移除,因此上面 sayWorld(),应该通过 (new Test())->sayWorld() 这种方式调用
- 静态属性和方法可以通过 self 关键字调用
就像其它所有的 PHP 静态变量一样,静态属性只能被初始化为一个字符值或一个常量,不能使用表达式。 所以你可以把静态属性初始化为整型或数组,但不能指向另一个变量或函数返回值,也不能指向一个对象。
抽象类
定义为抽象的类可能无法直接被实例化
,任何一个类, 如果它里面至少有一个方法是被声明为抽象的,那么这个类就必须被声明为抽象的。如果类方法被声明为抽象的, 那么其中就不能包括具体的功能实现。
编辑 /home/project/abstract.php
<?php
abstract class Say
{
abstract public function sayHello($word);
abstract public function sayHi();
}
class Speak extends Say
{
public function sayHello($word)
{
echo "Hello $word";
}
public function sayHi()
{
echo "Hi".PHP_EOL;
}
}
$s = new Speak();
$s->sayHi();
$s->sayHello("World");
执行
php abstract.php
从结果可以看出
- 继承一个抽象类的时候,子类必须定义父类中的所有抽象方法。例如,在类 Speak 中移除方法 sayHi(),结果为
Fatal error: Class Speak contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (Say::sayHi)...
- 这些方法的访问控制必须和父类中一样(或者更为宽松)。例如,在类 Speak 中 sayHi() 声明为 protected,则报错
Fatal error: Access level to Speak::sayHi() must be public (as in class Say)...
- 此外方法的调用方式必须匹配,即类型和所需参数数量必须一致。例如,移除抽象方法 sayHello() 中的参数,则
Fatal error: Declaration of Speak::sayHello($word) must be compatible with Say::sayHello()...
接口
使用接口(interface),你可以指定某个类必须实现哪些方法,但不需要定义这些方法的具体内容。我们可以通过 interface 来定义一个接口,就像定义一个标准的类一样,但其中定义所有的方法都是空的。 接口中定义的所有方法都必须是 public,这是接口的特性。
实现
要实现一个接口,可以使用 implements 操作符。类中必须实现接口中定义的所有方法,否则会报一个 fatal 错误。如果要实现多个接口,可以用逗号来分隔多个接口的名称。
<?php
interface A
{
public function actionA();
}
interface B
{
public function actionB();
}
//实现多个接口
class C implements A, B
{
public function actionA()
{
//do something
}
public function actionB()
{
//do something
}
}
注意:
- 实现多个接口时,接口中的方法不能有重名。
- 接口也可以继承,通过使用 extends 操作符。
<?php
interface A
{
public function actionA();
}
interface B extends A
{
public function actionB();
}
class C implements A
{
public function actionA()
{
//do something
}
public function actionB()
{
//do something
}
}
常量
接口中也可以定义常量。接口常量和类常量的使用完全相同。 它们都是定值,不能被子类或子接口修改。
<?php
interface A
{
const B = 'Interface constant';
}
// 输出接口常量
echo A::B;
// 错误写法,因为常量的值不能被修改。接口常量的概念和类常量是一样的。
class C implements A
{
const B = 'Class constant';
}
匿名类
php7 支持通过 new class 来实例化一个匿名类,这可以用来替代一些“用后即焚”的完整类定义。
<?php
interface Logger {
public function log(string $msg);
}
class Application {
private $logger;
public function getLogger(): Logger {
return $this->logger;
}
public function setLogger(Logger $logger) {
$this->logger = $logger;
}
}
$app = new Application;
$app->setLogger(new class implements Logger {
public function log(string $msg) {
echo $msg;
}
});
var_dump($app->getLogger());
?>
以上例程会输出:
object(class@anonymous)#2 (0) {
}