2024 年日本 PHP 研討會

基礎入門

class

基本的類別定義以關鍵字 class 開頭,接著是類別名稱,然後是一對大括號,其中包含屬於該類別的屬性和方法的定義。

類別名稱可以是任何有效的標籤,前提是它不是 PHP 的保留字。從 PHP 8.4.0 開始,使用單個底線 _ 作為類別名稱已被棄用。有效的類別名稱以字母或底線開頭,後跟任意數量的字母、數字或底線。以正規表達式表示,如下所示:^[a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*$

一個類別可以包含自己的常數變數(稱為「屬性」)和函式(稱為「方法」)。

範例 #1 簡單的類別定義

<?php
class SimpleClass
{
// 屬性宣告
public $var = 'a default value';

// 方法宣告
public function displayVar() {
echo
$this->var;
}
}
?>

當從物件上下文中呼叫方法時,可以使用偽變數 $this$this 是呼叫物件的值。

警告

靜態呼叫非靜態方法會擲出 Error。在 PHP 8.0.0 之前,這會產生一個棄用通知,並且 $this 將未定義。

範例 #2 一些 $this 偽變數的例子

<?php
class A
{
function
foo()
{
if (isset(
$this)) {
echo
'$this 已定義 (';
echo
get_class($this);
echo
")\n";
} else {
echo
"\$this 未定義。\n";
}
}
}

class
B
{
function
bar()
{
A::foo();
}
}

$a = new A();
$a->foo();

A::foo();

$b = new B();
$b->bar();

B::bar();
?>

上述範例在 PHP 7 中的輸出

$this is defined (A)

Deprecated: Non-static method A::foo() should not be called statically in %s  on line 27
$this is not defined.

Deprecated: Non-static method A::foo() should not be called statically in %s  on line 20
$this is not defined.

Deprecated: Non-static method B::bar() should not be called statically in %s  on line 32

Deprecated: Non-static method A::foo() should not be called statically in %s  on line 20
$this is not defined.

上述範例在 PHP 8 中的輸出

$this is defined (A)

Fatal error: Uncaught Error: Non-static method A::foo() cannot be called statically in %s :27
Stack trace:
#0 {main}
  thrown in %s  on line 27

唯讀類別

從 PHP 8.2.0 開始,類別可以使用 readonly 修飾符標記。將類別標記為 readonly 會將 readonly 修飾符 添加到每個已宣告的屬性,並防止建立 動態屬性。此外,無法透過使用 AllowDynamicProperties 屬性來新增對它們的支援。嘗試這樣做會觸發編譯時錯誤。

<?php
#[\AllowDynamicProperties]
readonly class
Foo {
}

// 致命錯誤:無法將 #[AllowDynamicProperties] 應用於唯讀類別 Foo
?>

由於無類型和靜態屬性都不能使用 readonly 修飾符標記,因此唯讀類別也不能宣告它們。

<?php
readonly class Foo
{
public
$bar;
}

// 致命錯誤:唯讀屬性 Foo::$bar 必須指定類型
?>
<?php
readonly class Foo
{
public static
int $bar;
}

// 致命錯誤:唯讀類別 Foo 不能宣告靜態屬性
?>

唯讀 (readonly) 類別可以被繼承,但前提是子類別也必須是唯讀 (readonly) 類別。

new

要建立類別的實例,必須使用 new 關鍵字。除非物件定義了一個在發生錯誤時拋出例外建構子,否則一定會建立一個物件。類別應該在實例化之前定義(在某些情況下,這是一項要求)。

如果一個變數包含一個字串,其中包含類別的名稱,並且與 new 一起使用,則會建立該類別的新實例。如果該類別位於命名空間中,則在執行此操作時必須使用其完整限定名稱。

注意事項:

如果沒有要傳遞給類別建構子的引數,則類別名稱後面的括號可以省略。

範例 #3 建立實例

<?php
$instance
= new SimpleClass();

// 這也可以用變數來完成:
$className = 'SimpleClass';
$instance = new $className(); // new SimpleClass()
?>

從 PHP 8.0.0 開始,支援將 new 與任意表達式一起使用。如果表達式產生一個字串,則允許更複雜的實例化。表達式必須用括號括起來。

範例 #4 使用任意表達式建立實例

在給定的範例中,我們展示了多個產生類別名稱的有效任意表達式的範例。這顯示了對函數的呼叫、字串串接和 ::class 常數。

<?php

class ClassA extends \stdClass {}
class
ClassB extends \stdClass {}
class
ClassC extends ClassB {}
class
ClassD extends ClassA {}

function
getSomeClass(): string
{
return
'ClassA';
}

var_dump(new (getSomeClass()));
var_dump(new ('Class' . 'B'));
var_dump(new ('Class' . 'C'));
var_dump(new (ClassD::class));
?>

上述範例在 PHP 8 中的輸出

object(ClassA)#1 (0) {
}
object(ClassB)#1 (0) {
}
object(ClassC)#1 (0) {
}
object(ClassD)#1 (0) {
}

在類別的上下文情境中,可以透過 `new self` 和 `new parent` 建立新的物件。

將已建立的類別實例賦值給新的變數時,新的變數將會存取與被賦值的物件相同的實例。這個行為在將實例傳遞給函式時也是一樣的。可以透過複製(cloning)一個已建立的物件來建立副本。

範例 #5 物件賦值

<?php

$instance
= new SimpleClass();

$assigned = $instance;
$reference =& $instance;

$instance->var = '$assigned will have this value';

$instance = null; // $instance 和 $reference 會變成 null

var_dump($instance);
var_dump($reference);
var_dump($assigned);
?>

上述範例會輸出

NULL
NULL
object(SimpleClass)#1 (1) {
   ["var"]=>
     string(30) "$assigned will have this value"
}

有幾種方法可以建立物件的實例

範例 #6 建立新的物件

<?php

class Test
{
public static function
getNew()
{
return new static();
}
}

class
Child extends Test {}

$obj1 = new Test(); // 以類別名稱建立
$obj2 = new $obj1(); // 透過包含物件的變數建立
var_dump($obj1 !== $obj2);

$obj3 = Test::getNew(); // 以類別方法建立
var_dump($obj3 instanceof Test);

$obj4 = Child::getNew(); // 透過子類別方法建立
var_dump($obj4 instanceof Child);

?>

上述範例會輸出

bool(true)
bool(true)
bool(true)

可以在單個表達式中存取新建物件的成員。

範例 #7 存取新建物件的成員

<?php
echo (new DateTime())->format('Y');
// 從 PHP 8.4.0 開始,外圍的括號是可選的
echo new DateTime()->format('Y');
?>

上述範例的輸出會類似於

2016

注意 在 PHP 7.1 之前,如果沒有定義建構函式,則不會評估參數。

屬性和方法

類別屬性和方法位於不同的「命名空間」中,因此可以同時擁有名稱相同的屬性和方法。引用屬性和方法的表示法相同,而究竟是存取屬性還是呼叫方法,完全取決於上下文,也就是用法是變數存取還是函式呼叫。

範例 #8 屬性存取 vs. 方法呼叫

<?php
class Foo
{
public
$bar = 'property';

public function
bar() {
return
'method';
}
}

$obj = new Foo();
echo
$obj->bar, PHP_EOL, $obj->bar(), PHP_EOL;

上述範例會輸出

property
method

這表示呼叫已賦值給屬性的匿名函式並非直接可行。相反地,屬性必須先賦值給一個變數。可以透過將此類屬性用括號括起來直接呼叫它。

範例 #9 呼叫儲存在屬性中的匿名函式

<?php
class Foo
{
public
$bar;

public function
__construct() {
$this->bar = function() {
return
42;
};
}
}

$obj = new Foo();

echo (
$obj->bar)(), PHP_EOL;

上述範例會輸出

42

繼承

類別可以使用關鍵字 extends 在類別宣告中繼承另一個類別的常數、方法和屬性。無法繼承多個類別;一個類別只能繼承自一個基底類別。

繼承的常數、方法和屬性可以透過在父類別中使用相同的名稱重新宣告它們來覆寫。但是,如果父類別已將方法或常數定義為 final,則它們可能無法被覆寫。可以透過使用 parent:: 參考它們來存取被覆寫的方法或靜態屬性。

注意 從 PHP 8.1.0 開始,常數可以宣告為 final。

範例 #10 簡單的類別繼承

<?php
class ExtendClass extends SimpleClass
{
// 重新定義父類別方法
function displayVar()
{
echo
"Extending class\n";
parent::displayVar();
}
}

$extended = new ExtendClass();
$extended->displayVar();
?>

上述範例會輸出

Extending class
a default value

簽章相容性規則

覆寫方法時,其簽章必須與父方法相容。否則,會產生致命錯誤,或者在 PHP 8.0.0 之前,會產生 E_WARNING 等級的錯誤。如果簽章遵循 變異數 規則,將必填參數設為選填,僅新增選填的新參數,並且不限制而僅放寬可見性,則該簽章是相容的。這稱為里氏替換原則,簡稱 LSP。建構子private 方法不受這些簽章相容性規則的約束,因此在簽章不匹配的情況下不會產生致命錯誤。

範例 #11 相容的子方法

<?php

class Base
{
public function
foo(int $a) {
echo
"Valid\n";
}
}

class
Extend1 extends Base
{
function
foo(int $a = 5)
{
parent::foo($a);
}
}

class
Extend2 extends Base
{
function
foo(int $a, $b = 5)
{
parent::foo($a);
}
}

$extended1 = new Extend1();
$extended1->foo();
$extended2 = new Extend2();
$extended2->foo(1);

上述範例會輸出

Valid
Valid

以下範例說明子類別方法若移除參數,或將選用參數改為必填,將與父類別方法不相容。

範例 #12 子類別方法移除參數時的致命錯誤

<?php

class Base
{
public function
foo(int $a = 5) {
echo
"Valid\n";
}
}

class
Extend extends Base
{
function
foo()
{
parent::foo(1);
}
}

以上範例在 PHP 8 中的輸出類似於

Fatal error: Declaration of Extend::foo() must be compatible with Base::foo(int $a = 5) in /in/evtlq on line 13

範例 #13 子類別方法將選用參數改為必填時的致命錯誤

<?php

class Base
{
public function
foo(int $a = 5) {
echo
"Valid\n";
}
}

class
Extend extends Base
{
function
foo(int $a)
{
parent::foo($a);
}
}

以上範例在 PHP 8 中的輸出類似於

Fatal error: Declaration of Extend::foo(int $a) must be compatible with Base::foo(int $a = 5) in /in/qJXVC on line 13
警告

在子類別中重新命名方法的參數名稱並不算簽章不相容。然而,不建議這樣做,因為如果使用了具名引數,將會導致執行階段錯誤

範例 #14 使用命名參數且子類別中參數已重新命名時發生的錯誤

<?php

class A {
public function
test($foo, $bar) {}
}

class
B extends A {
public function
test($a, $b) {}
}

$obj = new B;

// 根據 A::test() 的合約傳遞參數
$obj->test(foo: "foo", bar: "bar"); // 錯誤!

上述範例的輸出會類似於

Fatal error: Uncaught Error: Unknown named parameter $foo in /in/XaaeN:14
Stack trace:
#0 {main}
  thrown in /in/XaaeN on line 14

::class

關鍵字 class 也用於類別名稱解析。要取得類別 ClassName 的完整限定名稱,請使用 ClassName::class。這在使用 命名空間 的類別時特別有用。

範例 #15 類別名稱解析

<?php
namespace NS {
class
ClassName {
}

echo
ClassName::class;
}
?>

上述範例會輸出

NS\ClassName

注意事項:

使用 ::class 的類別名稱解析是編譯時期的轉換。這表示在建立類別名稱字串時,尚未進行自動載入。因此,即使類別不存在,類別名稱也會被展開。在這種情況下,不會發出錯誤。

範例 #16 遺漏的類別名稱解析

<?php
print Does\Not\Exist::class;
?>

上述範例會輸出

Does\Not\Exist

從 PHP 8.0.0 開始,::class 也可用於物件。此解析發生在執行時期,而不是編譯時期。其效果與在物件上呼叫 get_class() 相同。

範例 #17 物件名稱解析

<?php
namespace NS {
class
ClassName {
}
}
$c = new ClassName();
print
$c::class;
?>

上述範例會輸出

NS\ClassName

Nullsafe 方法和屬性

從 PHP 8.0.0 開始,也可以使用「nullsafe」運算子來存取屬性和方法:?->。 nullsafe 運算子的作用與上述的屬性或方法存取相同,但如果被解參考的物件是 null,則會回傳 null 而不是拋出例外。如果解參考是鏈的一部分,則會跳過鏈的其餘部分。

效果類似於先用 is_null() 檢查包裝每個存取,但更簡潔。

範例 #18 Nullsafe 運算子

<?php

// 從 PHP 8.0.0 開始,以下這行:
$result = $repository?->getUser(5)?->name;

// 等同於以下程式碼區塊:
if (is_null($repository)) {
$result = null;
} else {
$user = $repository->getUser(5);
if (
is_null($user)) {
$result = null;
} else {
$result = $user->name;
}
}
?>

注意事項:

當 null 被視為屬性或方法回傳值的有效且預期值時,最適合使用 null 安全運算子。若要表示錯誤,拋出例外會是更好的做法。

新增註解

使用者貢獻的註解 11 則註解

aaron at thatone dot com
16 年前
一開始我對物件賦值感到困惑,因為它與一般的賦值或透過參考賦值不太一樣。但我認為我已經搞清楚是怎麼回事了。

首先,將 PHP 中的變數想像成資料槽。每個變數都是一個名稱,指向一個可以保存值的資料槽,這些值可以是基本資料類型之一:數字、字串、布林值等。當您建立參考時,您正在建立第二個名稱,指向同一個資料槽。當您將一個變數賦值給另一個變數時,您正在將一個資料槽的內容複製到另一個資料槽。

現在,關鍵是物件實例與基本資料類型不同。它們不能直接保存在資料槽中。相反,物件的「控制代碼」會放入資料槽中。這是一個指向物件特定實例的識別碼。因此,物件控制代碼雖然程式設計師無法直接看到,但它是基本資料類型之一。

造成這種情況棘手的原因是,當您使用一個保存物件控制代碼的變數,並將其賦值給另一個變數時,另一個變數會獲得相同物件控制代碼的副本。這表示兩個變數都可以更改同一個物件實例的狀態。但它們不是參考,因此如果其中一個變數被賦予新值,它不會影響另一個變數。

<?php
// 物件的賦值
Class Object{
public
$foo="bar";
};

$objectVar = new Object();
$reference =& $objectVar;
$assignment = $objectVar

//
// $objectVar --->+---------+
// |(handle1)----+
// $reference --->+---------+ |
// |
// +---------+ |
// $assignment -->|(handle1)----+
// +---------+ |
// |
// v
// Object(1):foo="bar"
//
?>

$assignment 與 $objectVar 擁有不同的資料槽,但它的資料槽中存放的是指向同一個物件的控制代碼。這使得它在某些方面表現得像一個參考。如果您使用變數 $objectVar 來更改 Object 實例的狀態,這些更改也會在 $assignment 中顯示,因為它指向同一個 Object 實例。

<?php
$objectVar
->foo = "qux";
print_r( $objectVar );
print_r( $reference );
print_r( $assignment );

//
// $objectVar --->+---------+
// |(handle1)----+
// $reference --->+---------+ |
// |
// +---------+ |
// $assignment -->|(handle1)----+
// +---------+ |
// |
// v
// Object(1):foo="qux"
//
?>

但它與參考並不完全相同。如果您將 $objectVar 設為 null,您會將其資料槽中的控制代碼替換為 NULL。這意味著指向同一個資料槽的 $reference 也將為 NULL。但是 $assignment,它是一個不同的資料槽,仍將保留其 Object 實例控制代碼的副本,因此它不會是 NULL。

<?php
$objectVar
= null;
print_r($objectVar);
print_r($reference);
print_r($assignment);

//
// $objectVar --->+---------+
// | NULL |
// $reference --->+---------+
//
// +---------+
// $assignment -->|(handle1)----+
// +---------+ |
// |
// v
// Object(1):foo="qux"
?>
kStarbe at gmail point com
7 年前
您在第二個範例中開始使用 ::,儘管尚未解釋靜態概念。當您從基礎開始學習時,這並不容易發現。
Doug
14 年前
$this 和 self 之間有什麼區別?

在類別定義內,$this 指的是目前的物件,而 self 指的是目前的類別。

必須使用 self 來參考類別元素,
並使用 $this 來參考物件元素。
還要請注意,在物件變數的定義中,它前面必須加上一個關鍵字。

以下範例說明了一些情況

<?php
class Classy {

const
STAT = 'S' ; // 常數沒有錢字號 (它們永遠是靜態的)
static $stat = 'Static' ;
public
$publ = 'Public' ;
private
$priv = 'Private' ;
protected
$prot = 'Protected' ;

function
__construct( ){ }

public function
showMe( ){
print
'<br> self::STAT: ' . self::STAT ; // 像這樣參考 (靜態) 常數
print '<br> self::$stat: ' . self::$stat ; // 靜態變數
print '<br>$this->stat: ' . $this->stat ; // 合法,但與您想像的不同:空結果
print '<br>$this->publ: ' . $this->publ ; // 像這樣參考物件變數
print '<br>' ;
}
}
$me = new Classy( ) ;
$me->showMe( ) ;

/* 產生以下輸出:
self::STAT: S
self::$stat: Static
$this->stat:
$this->publ: Public
*/
?>
Hayley Watson
6 年前
類別名稱不區分大小寫
<?php
class Foo{}
class
foo{} //致命錯誤。
?>

任何大小寫都可用於引用類別
<?php
class bAr{}
$t = new Bar();
$u = new bar();
echo (
$t instanceof $u) ? "true" : "false"; // "true"
echo ($t instanceof BAR) ? "true" : "false"; // "true"
echo is_a($u, 'baR') ? "true" : "false"; // "true"
?>

但定義類別時使用的大小寫會保留為「標準」大小寫
<?php
echo get_class($t); // "bAr"
?>

此外,一如往常,「大小寫不敏感」僅適用於 ASCII。
<?php
class пасха{}
class
Пасха{} // 有效
$p = new ПАСХА(); // 未捕捉到的警告。
?>
wbcarts at juno dot com
16 年前
代表「理想世界」的類別和物件

如果能透過說 $son->mowLawn() 就能修剪草坪,那不是很棒嗎?假設定義了 mowLawn() 函式,而且您有一個不會拋出錯誤的兒子,那麼草坪就會被修剪。

在以下範例中,讓 Line3D 類型的物件自行測量其在三維空間中的長度。當類別本身擁有所有必要的數據並且具備自行計算的能力時,為什麼我或 PHP 必須從這個類別之外提供另一個方法來計算長度呢?

<?php

/*
* Point3D.php
*
* Represents one locaton or position in 3-dimensional space
* using an (x, y, z) coordinate system.
*/
class Point3D
{
public
$x;
public
$y;
public
$z; // the x coordinate of this Point.

/*
* use the x and y variables inherited from Point.php.
*/
public function __construct($xCoord=0, $yCoord=0, $zCoord=0)
{
$this->x = $xCoord;
$this->y = $yCoord;
$this->z = $zCoord;
}

/*
* the (String) representation of this Point as "Point3D(x, y, z)".
*/
public function __toString()
{
return
'Point3D(x=' . $this->x . ', y=' . $this->y . ', z=' . $this->z . ')';
}
}

/*
* Line3D.php
*
* Represents one Line in 3-dimensional space using two Point3D objects.
*/
class Line3D
{
$start;
$end;

public function
__construct($xCoord1=0, $yCoord1=0, $zCoord1=0, $xCoord2=1, $yCoord2=1, $zCoord2=1)
{
$this->start = new Point3D($xCoord1, $yCoord1, $zCoord1);
$this->end = new Point3D($xCoord2, $yCoord2, $zCoord2);
}

/*
* calculate the length of this Line in 3-dimensional space.
*/
public function getLength()
{
return
sqrt(
pow($this->start->x - $this->end->x, 2) +
pow($this->start->y - $this->end->y, 2) +
pow($this->start->z - $this->end->z, 2)
);
}

/*
* The (String) representation of this Line as "Line3D[start, end, length]".
*/
public function __toString()
{
return
'Line3D[start=' . $this->start .
', end=' . $this->end .
', length=' . $this->getLength() . ']';
}
}

/*
* create and display objects of type Line3D.
*/
echo '<p>' . (new Line3D()) . "</p>\n";
echo
'<p>' . (new Line3D(0, 0, 0, 100, 100, 0)) . "</p>\n";
echo
'<p>' . (new Line3D(0, 0, 0, 100, 100, 100)) . "</p>\n";

?>

<-- 結果如下所示 -->

Line3D[start=Point3D(x=0, y=0, z=0), end=Point3D(x=1, y=1, z=1), length=1.73205080757]

Line3D[start=Point3D(x=0, y=0, z=0), end=Point3D(x=100, y=100, z=0), length=141.421356237]

Line3D[start=Point3D(x=0, y=0, z=0), end=Point3D(x=100, y=100, z=100), length=173.205080757]

我最喜歡物件導向程式設計的一點是「好的」物件會自我約束。我的意思是,這與現實生活中完全一樣……例如,如果您聘請水電工修理廚房水槽,您難道不期望他能找出最佳的解決方案嗎?他難道不討厭您想要掌控整個工作嗎?您難道不期望他不會給您帶來額外的問題嗎?看在老天的份上,要求他在離開前清理乾淨會太過分嗎?

我認為,好好設計您的類別,讓它們可以不受干擾地完成工作……誰喜歡壞消息?而且,如果您的類別和物件定義良好、具備相關知識,並且擁有所有必要的數據可供使用(就像上面的例子一樣),您就不必從類別外部對整個程式進行微觀管理。換句話說……建立一個物件,然後讓它盡情發揮!
moty66 at gmail dot com
15 年前
我希望這有助於理解如何在類別內使用靜態變數

<?php

類別 a {

public static
$foo = 'I am foo';
public
$bar = 'I am bar';

public static function
getFoo() { echo self::$foo; }
public static function
setFoo() { self::$foo = 'I am a new foo'; }
public function
getBar() { echo $this->bar; }
}

$ob = new a();
a::getFoo(); // 輸出:I am foo
$ob->getFoo(); // 輸出:I am foo
//a::getBar(); // 致命錯誤:在非物件上下文中使用 $this
$ob->getBar(); // 輸出:I am bar
// 如果 $bar 不是靜態的,則此程式碼可以運作
// 但如果 bar 是靜態的,則 var_dump($this->bar) 將輸出 null

// unset($ob);
a::setFoo(); // 與呼叫 $ob->setFoo(); 的效果相同,因為 $foo 是靜態的
$ob = new a(); // 這不會對 $foo 產生任何影響
$ob->getFoo(); // 輸出:I am a new foo

?>

此致
Motaz Abuthiab
pawel dot zimnowodzki at gmail dot com
2 年前
雖然沒有針對不存在的陣列鍵的 null 安全運算子,但我找到了解決方法: ($array['not_existed_key'] ?? null)?->methodName()
關於 stdClass 的注意事項
15 年前
stdClass 是預設的 PHP 物件。 stdClass 沒有屬性、方法或父類別。它不支援魔術方法,也沒有實作任何介面。

當您將純量或陣列轉換為物件時,您會得到一個 stdClass 的實例。您可以在需要泛型物件實例時使用 stdClass。
<?php
// 建立 stdClass 實例的方法
$x = new stdClass;
$y = (object) null; // 與上述相同
$z = (object) 'a'; // 建立屬性 'scalar' = 'a'
$a = (object) array('property1' => 1, 'property2' => 'b');
?>

stdClass 並非基底類別!PHP 類別並不會自動繼承任何類別。所有類別都是獨立的,除非它們明確地繼承另一個類別。在這方面,PHP 與許多物件導向語言不同。
<?php
// CTest 並未繼承自 stdClass
class CTest {
public
$property1;
}
$t = new CTest;
var_dump($t instanceof stdClass); // false
var_dump(is_subclass_of($t, 'stdClass')); // false
echo get_class($t) . "\n"; // 'CTest'
echo get_parent_class($t) . "\n"; // false (沒有父類別)
?>

您無法在程式碼中定義名為 'stdClass' 的類別。該名稱已被系統使用。您可以定義一個名為 'Object' 的類別。

您可以定義一個繼承 stdClass 的類別,但您不會獲得任何好處,因為 stdClass 沒有任何作用。

(在 PHP 5.2.8 上測試)
johannes dot kingma at gmail dot com
3 年前
注意!

如同 Hayley Watson 指出的,類別名稱不區分大小寫。

<?php
class Foo{}
class
foo{} // 嚴重錯誤:無法宣告類別 foo,因為名稱已被使用
?>
以及
<?php
class BAR{}
$bar = new Bar();
echo
get_class($bar);
?>

完全沒問題,並且會返回 'BAR'。

然而,這對自動載入類別有影響。標準的 spl_autoload 函式會將類別名稱轉換為小寫以處理大小寫不敏感的情況,因此類別 BAR 只能在檔案名稱為 bar.php 時找到(或者如果使用 spl_autoload_extensions(); 註冊了副檔名,則可以使用其他變體),而不是 BAR.php(對於區分大小寫的檔案和作業系統,例如 Linux)。Windows 檔案系統區分大小寫,但作業系統不區分大小寫,因此自動載入 BAR.php 可以正常運作。
Jeffrey
16 年前
PHP 類別可以用於多種用途,但在最基本的層面上,您將使用類別來「組織和處理具有相似性質的資料」。以下是我所說的「組織具有相似性質的資料」的意思。首先,從未組織的資料開始。

<?php
$customer_name
;
$item_name;
$item_price;
$customer_address;
$item_qty;
$item_total;
?>

現在將資料組織到 PHP 類別中

<?php
class Customer {
$name; // 同 $customer_name
$address; // 同 $customer_address
}

class
Item {
$name; // 同 $item_name
$price; // 同 $item_price
$qty; // 同 $item_qty
$total; // 同 $item_total
}
?>

這裡我所說的「處理」數據是指:注意,數據已經被組織好了,這本身就讓撰寫新的函式變得非常容易。

<?php
class Customer {
public
$name, $address; // 這個類別的數據...

// 處理使用者輸入/驗證的函式
// 建立輸出字串的函式
// 寫入資料庫的函式
// 從資料庫讀取的函式
// 等等
}

class
Item {
public
$name, $price, $qty, $total; // 這個類別的數據...

// 計算總價的函式
// 格式化數字的函式
// 處理使用者輸入/驗證的函式
// 建立輸出字串的函式
// 寫入資料庫的函式
// 從資料庫讀取的函式
// 等等
}
?>

想像一下,你寫的每個函式都只會呼叫該類別中的數據。有些函式可能會存取所有數據,而其他函式可能只存取一個數據。如果每個函式都圍繞著內部的數據,那麼你就建立了一個好的類別。
匿名
6 年前
一開始我也對賦值和參考感到困惑,但以下是我最終理解的方式。這個例子與其中一個評論有點類似,但可能對不理解第一個例子的人有所幫助。想像物件實例就像房間,您可以在其中儲存和操作您的屬性和函式。包含物件的變數只是持有這個房間的「鑰匙」,因此可以存取物件。當您將這個變數賦值給另一個新變數時,您實際上是在複製鑰匙並將其交給這個新變數。這意味著這兩個變數現在都可以存取同一個「房間」(物件),因此可以進入並操作裡面的值。然而,當您建立參考時,您實際上是在讓變數共享同一把鑰匙。它們都可以存取房間。如果其中一個變數被賦予一把新鑰匙,那麼它們共享的鑰匙就會被替換,它們現在共享一把新的不同的鑰匙。這不會影響擁有舊鑰匙副本的另一個變數...該變數仍然可以存取第一個房間。
To Top