搜尋
首頁後端開發php教程類別與物件 - PHP手冊筆記

基本概念

PHP對待物件的方式與引用和句柄相同,即每個變數都持有物件的引用,而不是整個物件的拷貝。

當建立新物件時,該物件總是被賦值,除非該物件定義了建構函式並且在出錯時拋出了一個異常。類別應在被實例化之前定義。

建立物件時,如果該類別屬於一個名字空間,則必須使用其完整名稱。

在類別定義內部,可以用new selfnew parent建立物件。

<code><?php $instance = new stdClass();
$assigned = $instance;
$reference = & $instance;
$instance->var = '$assigned will have this value.';
$instance = null;
var_dump($instance);
var_dump($reference);
var_dump($assigned);</code>

這段程式碼的輸出如下,這是為什麼呢?

<code>null
null
object(stdClass)[1]
  public 'var' => string '$assigned will have this value.' (length=31)</code>

PHP 5.3引進了兩個新方法來建立一個物件的實例,可以使用下面的方法建立實例。

<code><?php class Test {
	static public function getNew() {
		return new static;
	}
}
class Child extends Test {}
$obj1 = new Test();
$obj2 = new $obj1;
var_dump($obj1 !== $obj2);  // true
$obj3 = Test::getNew();
var_dump($obj3 instanceof Test);  // true
$obj4 = Child::getNew();
var_dump($obj4 instanceof Child);  // true
var_dump($obj1 == $obj2);  // true</code></code>

PHP不支援多重繼承,被繼承的方法和屬性可以透過同樣的名字重新宣告被覆寫,注意參數必須保持一致,當然建構子除外。但是如果父類別定義方法時使用了final,則該方法不可被覆寫。可以透過parent::來存取被覆蓋的方法和屬性,parent::只能存取父類別中的常數const,不能存取變數。

<code><?php class A {
	private $name = &#39;A&#39;;
	const conname = &#39;A&#39;;
	public function getName() {
		return $this->name;
	}
}
class B extends A {
	private $name = 'B';
	const conname = 'B';
	public function getName() {
		return $this->name;
	}
	public function getParent() {
		return parent::conname;
	}
}
class C extends B {
	private $name = 'C';
	const conname = 'C';
	public function getName() {
		return $this->name;
	}
	public function getParent() {
		return parent::conname;
	}
}
$a = new A;
var_dump($a->getName());  // A
$b = new B;
var_dump($b->getName());  // B
var_dump($b->getParent());  // A
$c = new C;
var_dump($c->getName());  // C
var_dump($c->getParent());  // B</code>

自PHP 5.5起,關鍵字class也可用於類別名稱的解析。使用ClassName::class你可以取得一個字串,包含了類別ClassName的完全限定名稱。

<code><?php namespace NS {
	class ClassName {}
	echo ClassName::class;  // NS\ClassName
}</code></code>

屬性

屬性,也就是類別的變數成員。屬性中的變數可以初始化,但是初始化的值必須是常數。這裡的常數是指 PHP 腳本在編譯階段時就可以得到其值,而不依賴執行時的資訊才能求值。

在類別的成員方法裡,存取非靜態屬性使用$this->property,存取靜態屬性使用self::$property。靜態屬性聲明時使用static關鍵字。

類常數

在定義常數時不需要$符號和存取控製關鍵字。

介面(interface)中也可以定義常數。

自動載入類別

寫物件導向的應用程式時,通常對每個類別的定義履歷表一個PHP來源檔案。當某個檔案需要呼叫這些類別時,需要在檔案開頭寫一個長長的包含檔案清單。其實,並不需要這樣,可以定義一個__autoload()函數,它會在試圖使用尚未被定義的類別時自動呼叫。

手冊Tip說,spl_autoload_register()提供了一種更靈活的方式來實現類別的自動加載,這個後面再看。

自動載入不可用於PHP的CLI互動模式,也就是命令列模式。

用戶輸入中可能存在危險字符,起碼要在__autoload()時驗證下輸入。

可以透過下面的方式自動載入類別。

<code><?php function __autoload($class_name) {
	require_once $class_name.&#39;.php&#39;;
}
$obj1 = new MyClass1();
$obj2 = new MyClass2();</code></code>

對於異常處理,後面再看。

建構子和析構函式

PHP 5允許開發者在一個類別中定義一個方法作為建構函數,建構函數也不支援重載。

如果子類別中定義了建構函數,則不會隱式呼叫父類別的建構函數,否則會如同一個普通類別方法那樣從父類別繼承(前提是未被定義為private)。要執行父類別的建構函數,需要在子類別建構子中呼叫parent::__construct()

與其它方法不同,當__construct()與父類__construct()具有不同參數時,可以覆蓋。

自PHP 5.3.3起,在命名空間中,與類別名稱同名的方法不再作為建構子。

析構函數會在某個物件的所有參考都被刪除或物件被顯示銷毀時執行。析構函數即使在使用exit()終止腳本執行時也會被呼叫。

試圖在析構函數中拋出異常,將會導致致命錯誤。

門禁控制

類別屬性必須定義為公有、受保護、私有之一,且不能省略關鍵字。如果類別中方法沒有設定存取控制的關鍵字,則該方法預設為公有。

同一個類別的對象,即使不是同一個實例,也可以互相存取對方的私有與保護成員。範例程式如下。

<code><?php Class Test {
	private $foo;
	public function __construct($foo) {
		$this->foo = $foo;
	}
	private function bar() {
		echo 'Accessed the private method.';
	}
	public function baz(Test $other) {
		$other->foo = 'hello';
		var_dump($other->foo);
		$other->bar();
	}
}
$test = new Test('test');
$test->baz(new Test('other'));</code>

對象繼承

如果一個類別擴展了另一個,則父類別必須在子類別前被聲明。

範圍解析操作符

範圍解析運算符,簡單來說就是一對冒號,可以用來存取靜態成員、類別常數,也可以用來呼叫父類別中的屬性和方法。

當在類別定義之外引用這些項目時,要使用類別名稱。

static

使用static關鍵字可以用來定義靜態方法和屬性,也可用於定義靜態變數以及後期靜態綁定。聲明類別屬性或方法為靜態,就可以不實例化類別而直接存取。

靜態屬性不能透過一個類別已實例化的物件來訪問,但靜態方法可以。

如果沒有指定存取控制,屬性和方法預設為公有。

用靜態方法呼叫一個非靜態方法會導致一個E_STRICT等級的錯誤。

抽象类

PHP 5支持抽象类和抽象方法。类中如果有一个抽象方法,那这个类必须被声明为抽象的。

抽象类不能被实例化。抽象方法只是声明了其调用方式(参数),不能定义其具体的功能实现。继承抽象类时,子类必须定义父类中的所有抽象方法,且这些方法的访问控制必须和父类一样活更宽松。

方法的调用方式必须匹配。但是,子类定义了一个可选参数,而父类抽象方法的声明里没有,则两者的声明并无冲突。这也试用与PHP 5.4起的构造函数。可以在子类中定义父类签名中不存在的可选参数。

<code><?php abstract class AbstractClass {
	abstract protected function prefixName($name);
}
class ConcreteClass extends AbstractClass {
	public function prefixName($name, $separator = &#39;, &#39;) {
		if($name === "Pacman") {
			$prefix = &#39;Mr&#39;;
		} elseif($name === &#39;Pacwoman&#39;) {
			$prefix = "Mrs";
		} else {
			$prefix = &#39;&#39;;
		}
		return "$prefix $separator $name  ";
	}
}
$class = new ConcreteClass;
echo $class->prefixName('Pacman');
echo $class->prefixName('Pacwoman');</code>

对象接口

听说过接口,一直没用过。使用接口,可以指定某个类必须实现哪些方法,但不需要定义这些方法的具体内容,也就是说接口中定义的所有方法都是空的。接口中定义的所有方法都必须是公有的,这是接口的特性。

接口也可以继承多个接口,用逗号分隔,使用extends操作符。类中必须实现接口中定义的所有方法,否则会报错。要实现一个接口,使用implements操作符。类可以实现多个接口,用逗号分隔。实现多个接口时,接口中的方法不能有重名。类要实现接口,必须使用和接口中所定义的方法完全一致的方式。

接口中也可定义常量。接口常量和类常量的使用完全相同,但是不能被子类或子接口覆盖。

traits

从PHP 5.4.0开始,可以使用traits实现代码复用。Traits 是一种为类似 PHP 的单继承语言而准备的代码复用机制。Trait 不能通过它自身来实例化。它为传统继承增加了水平特性的组合。

优先顺序是来自当前类的成员覆盖了 trait 的方法,而 trait 则覆盖了被继承的方法。

通过逗号分隔,在 use 声明列出多个 trait,可以都插入到一个类中。如果两个 trait 都插入了一个同名的方法,如果没有明确解决冲突将会产生一个致命错误,为解决冲突,需使用insteadof操作符来指明使用冲突方法中的哪一个,这种方法仅允许排除掉其它方法。as操作符可以将其中一个冲突的方法以另一个名称(别名)来引入。

<code><?php trait A {
	public function smallTalk() {
		echo &#39;a&#39;;
	}
	public function bigTalk() {
		echo &#39;A&#39;;
	}
}
trait B {
	public function smallTalk() {
		echo &#39;b&#39;;
	}
	public function bigTalk() {
		echo &#39;B&#39;;
	}
}
class Talker {
	use A, B {
		B::smallTalk insteadof A;
		A::bigTalk insteadof B;
		B::bigTalk as talk;
	}
}
$t = new Talker();
$t->smallTalk();  // b
$t->bigTalk();  // A
$t->talk();  // B</code>

使用as操作符还可以用来调整方法的访问控制,或者给方法一个改变了访问控制的别名,原版方法的访问控制规则没有改变。

<code><?php trait HelloWorld {
	public function sayHello() {
		echo &#39;Hello World.&#39;;
	}
}
class MyClass1 {
	use HelloWorld {
		sayHello as protected;
	}
}
class MyClass2 {
	use HelloWorld {
		sayHello as private myPrivateHello;
	}
}</code></code>

就像类能够使用trait那样,多个trait能够组合为一个trait

为了对使用的类施加强制要求,trait 支持抽象方法的使用。

<code><?php trait Hello {
	public function sayHelloWorld() {
		echo &#39;Hello &#39; . $this->getWorld();
	}
	abstract public function getWorld();
}
class MyHelloWorld {
	private $world;
	use Hello;
	public function getWorld() {
		return $this->world;
	}
	public function setWorld($val) {
		$this->world = $val;
	}
}
$c = new MyHelloWorld;
$c->setWorld('world');
$c->sayHelloWorld();</code>

如果trait定义了一个属性,那类将不能定义同样名称的属性,否则会产生错误。

重载

PHP提供的重载是指动态地创建类属性和方法,与其它绝大多数面向对象语言不同。通过魔术方法来实现。当使用不可访问的属性或方法时,重载方法会被调用。所有的重载方法都必须被声明为public

使用__get()__set()__isset()__unset()进行属性重载,示例如下。

<code><?php class PropertyTest {
	private $data = array();
	public $declared = 1;
	private $hidden = 2;
	public function __set($name, $value) {
		echo "Setting $name to $value. " . &#39;<br>';
		$this->data[$name] = $value;
	}
	public function __get($name) {
		echo "Getting $name. <br>";
		if(array_key_exists($name, $this->data)) {
			return $this->data[$name];
		}
		return null;
	}
	public function __isset($name) {
		echo "Is $name set? <br>";
		return isset($this->data[$name]);
	}
	public function __unset($name) {
		echo "Unsetting $name. <br>";
		unset($this->data[$name]);
	}
}
$obj = new PropertyTest;
$obj->a = 1;
var_dump($obj->a);
var_dump(isset($obj->a));
unset($obj->a);
var_dump(isset($obj->a));
var_dump($obj->declared);
var_dump($obj->hidden);</code>

输出结果如下:

<code>Setting a to 1. 
Getting a. 
int 1
Is a set? 
boolean true
Unsetting a. 
Is a set? 
boolean false
int 1
Getting hidden. 
null</code>

在对象中调用一个不可访问方法时,__call()会被调用。用静态方式中调用一个不可访问方法时,__callStatic()会被调用。参数为调用方法的名称和一个枚举数组,注意区分大小写。

使用__call()__callStatic()对方法重载,示例如下。

<code><?php class MethodTest {
	public function __call($name, $arguments) {
		echo "Calling object method $name " . 
			implode(&#39;, &#39;, $arguments) . &#39;<br>';
	}
	public static function __callStatic($name, $arguments) {
		echo "Calling static method $name " . 
			implode(', ', $arguments) . '<br>';
	}
}
$obj = new MethodTest;
$obj->runTest('in object context');
MethodTest::runTest('in static context');</code>

遍历对象

对象可以用过单元列表来遍历,例如用foreach语句。默认所有可见属性都将被用于遍历。

<code><?php class MyClass {
	public $var1 = &#39;value 1&#39;;
	public $var2 = &#39;value 2&#39;;
	public $var3 = &#39;value 3&#39;;
	private $var4 = &#39;value 4&#39;;
	protected $var5 = &#39;value 5&#39;;
}
$obj = new MyClass;
foreach($obj as $key => $value) {
	echo "$key => $value <br>";
}</code>

示例程序2实现了Iterator接口的对象遍历,示例程序3通过实现IteratorAggregate来遍历对象。

魔术方法

PHP 将所有以__(两个下划线)开头的类方法保留为魔术方法。定义类方法时,除魔术方法外,建议不要以__为前缀。

前面遇到过的魔术方法有:__construct()__destruct()__call()__callStatic()__get()__set()__isset()__unset()。后面将会介绍:__sleep()__wakeup()__toString()__invoke()__set_state()__clone()__debugInfo()

__sleep__wakeup不清楚具体做什么用的,示例程序中给出了个数据库连接的例子。

__toString方法用于一个类被当成字符串时应怎样回应。此方法必须返回一个字符串,且不能再方法中抛出异常。如果将一个未定义__toString()方法的对象转换为字符串,将产生错误。

当尝试以调用函数的方式调用一个对象时,__invoke()方法会被调用。

当调用var_export()导出类时,__set_state()会被调用。

当调用var_dump()时,__debugInfo会被调用。PHP 5.6新加入,没合适的环境无法测试。

final

果父类中的方法被声明为final,则子类无法覆盖该方法。如果一个类被声明为final,则不能被继承。属性不能被定义为final,只有类和方法才能被定义为final

对象复制

多数情况,我们不需要完全复制一个对象,但有时确实需要。对象复制可以通过clone关键字来完成。这种复制是通过调用对象的__clone()方法实现的,但是对象中的__clone()方法不能被直接调用。

对象比较

比较运算符==为真的条件是:两个对象的属性和属性值都相等,而且两个对象是同一个类的实例。

继承与统一个基类的两个子类的对象不会相等==

<code><?php class Base {}
class A extends Base {}
class B extends Base {}
$a = new A;
$b = new B;
var_dump($a == $b);  // false</code></code>

全等运算符===为真的条件是:两个对象变量一定要指向某个类的同一个实例(即同一个对象)。

类型约束

类型约束是指函数的参数可以指定必须为对象、接口、数组或者callable类型。但是类型约束不能用于标量类型如intstringtraits也不允许。类型约束允许NULL值。

后期静态绑定

后期静态绑定,用于在继承范围内引用静态调用的类。

转发调用,指的是通过以下几种方式进行的静态调用:self::parent::static::以及forward_static_call()

后期静态绑定的工作原理是,存储了上一个非转发调用的类名。

当进行静态方法调用时,该类名即为明确指定的那个;当进行非静态方法调用时,即为该对象所属的类。

使用self::或者__CLASS__对当前类的静态引用,取决于定义当前方法所在的类。

<code><?php class A {
	public static function who() {
		echo __CLASS__;
	}
	public static function test() {
		self::who();
	}
}
class B extends A {
	public static function who() {
		echo __CLASS__;
	}
}
B::test();  // A
B::who();  // B</code></code>

static::关键字表示运行时最初调用的类,后期静态绑定就是这样使用。如下面程序所示,也就是说调用test()时引用的类是B而不是A

<code><?php class A {
	public static function who() {
		echo __CLASS__;
	}
	public static function test() {
		static::who();
	}
}
class B extends A {
	public static function who() {
		echo __CLASS__;
	}
}
B::test();  // B
B::who();  // B</code></code>

示例2给出的是非静态环境下使用static::

后期静态绑定的解析,会一直到取得一个完全解析了的静态调用为止。另外,如果静态调用使用parent::self::将转发调用信息。

<code><?php class A {
	public static function foo() {
		static::who();
	}
	public static function who() {
		echo __CLASS__;
	}
}
class B extends A {
	public static function test() {
		A::foo();
		parent::foo();
		self::foo();
	}
	public static function who() {
		echo __CLASS__;
	}
}
class C extends B {
	public static function who() {
		echo __CLASS__;
	}
}
C::test();  // ACC</code></code>

那么问题来了,结果为什么是这样的呢?

对象和引用

默认情况下,对象时通过引用传递的。但这种说法不完全正确,其实两个对象变量不是引用的关系,只是他们都保存着同一个标识符的拷贝,这个标识符指向同一个对象的真正内容。

对象序列化

所有PHP里面的值,都可以使用函数serialize()来返回一个包含字节流的字符串来表示。unserialize()函数能够重新把字符串变为原来的值。

序列化一个对象,将会保存对象的所有变量,但是不会保存对象的方法,只会保存类的名字。为了能够unserialize()一个对象,这个对象的类必须已经定义过。在应用程序中序列化对象以便在之后使用,强烈推荐在整个应用程序都包含对象的类的定义。

(全文完)

以上就介绍了类与对象 - PHP手册笔记,包括了方面的内容,希望对PHP教程有兴趣的朋友有所帮助。

陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
PHP:服務器端腳本語言的簡介PHP:服務器端腳本語言的簡介Apr 16, 2025 am 12:18 AM

PHP是一種服務器端腳本語言,用於動態網頁開發和服務器端應用程序。 1.PHP是一種解釋型語言,無需編譯,適合快速開發。 2.PHP代碼嵌入HTML中,易於網頁開發。 3.PHP處理服務器端邏輯,生成HTML輸出,支持用戶交互和數據處理。 4.PHP可與數據庫交互,處理表單提交,執行服務器端任務。

PHP和網絡:探索其長期影響PHP和網絡:探索其長期影響Apr 16, 2025 am 12:17 AM

PHP在過去幾十年中塑造了網絡,並將繼續在Web開發中扮演重要角色。 1)PHP起源於1994年,因其易用性和與MySQL的無縫集成成為開發者首選。 2)其核心功能包括生成動態內容和與數據庫的集成,使得網站能夠實時更新和個性化展示。 3)PHP的廣泛應用和生態系統推動了其長期影響,但也面臨版本更新和安全性挑戰。 4)近年來的性能改進,如PHP7的發布,使其能與現代語言競爭。 5)未來,PHP需應對容器化、微服務等新挑戰,但其靈活性和活躍社區使其具備適應能力。

為什麼要使用PHP?解釋的優點和好處為什麼要使用PHP?解釋的優點和好處Apr 16, 2025 am 12:16 AM

PHP的核心優勢包括易於學習、強大的web開發支持、豐富的庫和框架、高性能和可擴展性、跨平台兼容性以及成本效益高。 1)易於學習和使用,適合初學者;2)與web服務器集成好,支持多種數據庫;3)擁有如Laravel等強大框架;4)通過優化可實現高性能;5)支持多種操作系統;6)開源,降低開發成本。

揭穿神話:PHP真的是一種死語嗎?揭穿神話:PHP真的是一種死語嗎?Apr 16, 2025 am 12:15 AM

PHP沒有死。 1)PHP社區積極解決性能和安全問題,PHP7.x提升了性能。 2)PHP適合現代Web開發,廣泛用於大型網站。 3)PHP易學且服務器表現出色,但類型系統不如靜態語言嚴格。 4)PHP在內容管理和電商領域仍重要,生態系統不斷進化。 5)通過OPcache和APC等優化性能,使用OOP和設計模式提升代碼質量。

PHP與Python辯論:哪個更好?PHP與Python辯論:哪個更好?Apr 16, 2025 am 12:03 AM

PHP和Python各有優劣,選擇取決於項目需求。 1)PHP適合Web開發,易學,社區資源豐富,但語法不夠現代,性能和安全性需注意。 2)Python適用於數據科學和機器學習,語法簡潔,易學,但執行速度和內存管理有瓶頸。

PHP的目的:構建動態網站PHP的目的:構建動態網站Apr 15, 2025 am 12:18 AM

PHP用於構建動態網站,其核心功能包括:1.生成動態內容,通過與數據庫對接實時生成網頁;2.處理用戶交互和表單提交,驗證輸入並響應操作;3.管理會話和用戶認證,提供個性化體驗;4.優化性能和遵循最佳實踐,提升網站效率和安全性。

PHP:處理數據庫和服務器端邏輯PHP:處理數據庫和服務器端邏輯Apr 15, 2025 am 12:15 AM

PHP在數據庫操作和服務器端邏輯處理中使用MySQLi和PDO擴展進行數據庫交互,並通過會話管理等功能處理服務器端邏輯。 1)使用MySQLi或PDO連接數據庫,執行SQL查詢。 2)通過會話管理等功能處理HTTP請求和用戶狀態。 3)使用事務確保數據庫操作的原子性。 4)防止SQL注入,使用異常處理和關閉連接來調試。 5)通過索引和緩存優化性能,編寫可讀性高的代碼並進行錯誤處理。

您如何防止PHP中的SQL注入? (準備的陳述,PDO)您如何防止PHP中的SQL注入? (準備的陳述,PDO)Apr 15, 2025 am 12:15 AM

在PHP中使用預處理語句和PDO可以有效防範SQL注入攻擊。 1)使用PDO連接數據庫並設置錯誤模式。 2)通過prepare方法創建預處理語句,使用佔位符和execute方法傳遞數據。 3)處理查詢結果並確保代碼的安全性和性能。

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
4 週前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
4 週前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您聽不到任何人,如何修復音頻
4 週前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.聊天命令以及如何使用它們
4 週前By尊渡假赌尊渡假赌尊渡假赌

熱工具

mPDF

mPDF

mPDF是一個PHP庫,可以從UTF-8編碼的HTML產生PDF檔案。原作者Ian Back編寫mPDF以從他的網站上「即時」輸出PDF文件,並處理不同的語言。與原始腳本如HTML2FPDF相比,它的速度較慢,並且在使用Unicode字體時產生的檔案較大,但支援CSS樣式等,並進行了大量增強。支援幾乎所有語言,包括RTL(阿拉伯語和希伯來語)和CJK(中日韓)。支援嵌套的區塊級元素(如P、DIV),

Atom編輯器mac版下載

Atom編輯器mac版下載

最受歡迎的的開源編輯器

EditPlus 中文破解版

EditPlus 中文破解版

體積小,語法高亮,不支援程式碼提示功能

PhpStorm Mac 版本

PhpStorm Mac 版本

最新(2018.2.1 )專業的PHP整合開發工具

WebStorm Mac版

WebStorm Mac版

好用的JavaScript開發工具