<基础> PHP 进阶之 类(Class)
属性
类的变量成员叫做“属性”,或者叫“字段”、“特征”,在本文档统一称为“属性”。属性声明是由关键字 public,protected 或者 private 开头,然后跟一个普通的变量声明来组成。属性中的变量可以初始化,但是初始化的值必须是常数,这里的常数是指 PHP 脚本在编译阶段时就可以得到其值,而不依赖于运行时的信息才能求值。
在类的成员方法里面,可以用 ->(对象运算符):$this->property(其中 property 是该属性名)这种方式来访问非静态属性。静态属性则是用 ::(双冒号):self::$property 来访问
类常量
可以把在类中始终保持不变的值定义为常量。在定义和使用常量的时候不需要使用 $ 符号。
常量的值必须是一个定值,不能是变量,类属性,数学运算的结果或函数调用,可以是数组
class Test { const COLOR = array('yellow','white','black'); function showColor(){ var_dump(self::COLOR); } }
- 常量与属性一样,可以被继承
- 接口中也可以定义常量
自动加载
在 PHP 5 中, spl_autoload_register() 函数可以注册任意数量的自动加载器,当使用尚未被定义的类(class)和接口(interface)时自动去加载。通过注册自动加载器,脚本引擎在 PHP 出错失败前有了最后一个机会加载所需的类。
spl_autoload_register() 参数是一个回调函数,可以注册多次,例如
<?php // MyClass 位于 ./MyClass/MyClass.php 下,同时MyClass继承 ./MyClass1/MyClass1.php中的 MyClass1 function load_MyClass($class_name) { // 引用前一定要判断文件是否存在,避免文件不存在报错 if (file_exists('MyClass/' . $class_name . '.php')){ require_once 'MyClass/' . $class_name . '.php'; } } function load_MyClass1($class_name) { if (file_exists('MyClass1/' . $class_name . '.php')){ require_once 'MyClass1/' . $class_name . '.php'; } } spl_autoload_register('load_MyClass'); spl_autoload_register('load_MyClass1'); $c = new MyClass(); $c->say();
构造函数
public function __construct ([ mixed $args [, $... ]] )
- 如果子类中定义了构造函数则不会隐式调用其父类的构造函数。要执行父类的构造函数,需要在子类的构造函数中调用parent::__construct()。
- 如果子类没有定义构造函数则会如同一个普通的类方法一样从父类继承(假如没有被定义为 private 的话)。
- 子类构造函数参数可以和父类中不一样 (其他方法不行)
- 构造函数中不能使用 return 返回结果,因为这样会终结此次请求
析构函数
public function __destruct ( void )
析构函数即使在使用 exit() 终止脚本运行时也会被调用。在析构函数中调用 exit() 将会中止其余关闭操作的运行。
继承
当扩展一个类,子类就会继承父类所有公有的和受保护的方法。除非子类覆盖了父类的方法,被继承的方法都会保留其原有功能。
- 除非使用了自动加载,否则一个类必须在使用之前被定义。如果一个类扩展了另一个,则父类必须在子类之前被声明。此规则适用于类继承其它类与接口。
- PHP 不支持多继承 , 但不意味着子类不能继承多个父类,我们可以通过继承一个父类,父类继承另一个父类的方法实现
class A { // more code here } class B extends A { // more code here } class C extends B { // more code here }
重写
当一个子类覆盖其父类中的方法时,PHP 不会调用父类中已被覆盖的方法。是否调用父类的方法取决于子类。子类可以通过 parent 手动调用
class MyClass { protected function myFunc() { echo "MyClass::myFunc()\n"; } } class OtherClass extends MyClass { // 覆盖了父类的定义 public function myFunc() { // 但还是可以调用父类中被覆盖的方法 parent::myFunc(); echo "OtherClass::myFunc()\n"; } }
范围解析操作符
范围解析操作符(也可称作 Paamayim Nekudotayim)或者更简单地说是一对冒号,可以用于访问静态成员,类常量,还可以用于覆盖类中的属性和方法
当在类定义之外引用到这些项目时,要使用类名
静态属性 Static 关键字
就像其它所有的 PHP 静态变量一样,静态属性只能被初始化为文字或常量,不能使用表达式。所以可以把静态属性初始化为整数或数组,但不能初始化为另一个变量或函数返回值,也不能指向一个对象
调用静态属性:self:: static::
区别
- self 指向定义时指向的class
- static 指向调用时的class , 作用于$this 一样 ,但是$this 不能调用静态属性和方法
class a{ static protected $test="class a"; public function static_test(){ echo static::$test; // Results class b echo self::$test; // Results class a } } class b extends a{ static protected $test="class b"; } $obj = new b(); $obj->static_test();
匿名类
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; // 使用 new class 创建匿名类 $app->setLogger(new class implements Logger { public function log(string $msg) { print($msg); } }); $app->getLogger()->log("我的第一条日志"); ?>
Final
PHP 5 新增了一个 final 关键字。如果父类中的方法被声明为 final,则子类无法覆盖该方法。如果一个类被声明为 final,则不能被继承。
<?php final class BaseClass { public function test() { echo "BaseClass::test() called\n"; } // 这里无论你是否将方法声明为final,都没有关系 final public function moreTesting() { echo "BaseClass::moreTesting() called\n"; } } class ChildClass extends BaseClass { } // 产生 Fatal error: Class ChildClass may not inherit from final class (BaseClass) ?>
对象序列化
所有php里面的值都可以使用函数serialize()来返回一个包含字节流的字符串来表示。unserialize()函数能够重新把字符串变回php原来的值。 序列化一个对象将会保存对象的所有变量,但是不会保存对象的方法,只会保存类的名字。
注意
- 为了能够unserialize()一个对象,这个对象的类必须已经定义过
- 如果要想在另外一个文件中解序列化一个对象,这个对象的类必须在解序列化之前定义,可以通过包含一个定义该类的文件或使用函数spl_autoload_register()来实现。
serialize() 函数会检查类中是否存在一个魔术方法 __sleep()。如果存在,该方法会先被调用,然后才执行序列化操作。此功能可以用于清理对象,并返回一个包含对象中所有应被序列化的变量名称的数组。如果该方法未返回任何内容,则 NULL
被序列化,并产生一个 E_NOTICE
级别的错误。
__sleep() 方法常用于提交未提交的数据,或类似的清理操作。同时,如果有一些很大的对象,但不需要全部保存,这个功能就很好用。
__wakeup() 经常用在反序列化操作中,例如重新建立数据库连接,或执行其它初始化操作。
class Connection { protected $link; private $server, $username, $password, $db; public function __construct($server, $username, $password, $db) { $this->server = $server; $this->username = $username; $this->password = $password; $this->db = $db; $this->connect(); } private function connect() { $this->link = mysql_connect($this->server, $this->username, $this->password); mysql_select_db($this->db, $this->link); } public function __sleep() { return array('server', 'username', 'password', 'db'); } public function __wakeup() { $this->connect(); } }