PHP Conference Japan 2024

基础

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 is defined (';
echo
get_class($this);
echo
")\n";
} else {
echo
"\$this is not defined.\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 {
}

// Fatal error: Cannot apply #[AllowDynamicProperties] to readonly class Foo
?>

由于既不是无类型也不是静态属性可以用readonly修饰符标记,因此只读类也不能声明它们。

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

// Fatal error: Readonly property Foo::$bar must have type
?>
<?php
readonly class Foo
{
public static
int $bar;
}

// Fatal error: Readonly class Foo cannot declare static properties
?>

如果且仅当子类也是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

ClassA 扩展 \stdClass {}
ClassB 扩展 \stdClass {}
ClassC 扩展 ClassB {}
ClassD 扩展 ClassA {}

函数
getSomeClass(): string
{
返回
'ClassA';
}

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

PHP 8 中上述示例的输出

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

在类上下文中,可以通过 new selfnew parent 创建新的对象。

当将已创建的类的实例赋值给新的变量时,新变量将访问与被赋值的对象相同的实例。此行为在将实例传递给函数时也是相同的。可以通过 克隆 已创建的对象来创建其副本。

示例 #5 对象赋值

<?php

$instance
= 新 SimpleClass();

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

$instance->var = '$assigned 将具有此值';

$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

Test
{
公共静态函数
getNew()
{
返回新 static();
}
}

Child 扩展 Test {}

$obj1 = 新 Test(); // 通过类名
$obj2 = 新 $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
回显 (新 DateTime())->format('Y');
// 从 PHP 8.4.0 开始,周围的括号是可选的
回显 新 DateTime()->format('Y');
?>

上面的示例将输出类似以下内容

2016

注意: 在 PHP 7.1 之前,如果没有定义构造函数,则不会评估参数。

属性和方法

类属性和方法存在于单独的“命名空间”中,因此可以拥有同名的属性和方法。引用属性和方法的表示法相同,并且访问属性还是调用方法完全取决于上下文,即用法是变量访问还是函数调用。

示例 #8 属性访问与方法调用

<?php
Foo
{
公共
$bar = 'property';

公共函数
bar() {
返回
'method';
}
}

$obj = 新 Foo();
回显
$obj->bar, PHP_EOL, $obj->bar(), PHP_EOL;

上面的示例将输出

property
method

这意味着无法直接调用已分配给属性的 匿名函数。相反,必须首先将属性分配给变量,例如。可以通过将其括在括号中来直接调用此类属性。

示例 #9 调用存储在属性中的匿名函数

<?php
Foo
{
公共
$bar;

公共函数
__construct() {
$this->bar = 函数() {
返回
42;
};
}
}

$obj = 新 Foo();

回显 (
$obj->bar)(), PHP_EOL;

上面的示例将输出

42

扩展

类可以通过在类声明中使用关键字 extends 继承另一个类的常量、方法和属性。无法扩展多个类;一个类只能继承自一个基类。

可以通过重新声明在父类中定义的同名继承的常量、方法和属性来覆盖它们。但是,如果父类已将方法或常量定义为 final,则不能覆盖它们。可以通过使用 parent:: 引用它们来访问被覆盖的方法或静态属性。

注意: 从 PHP 8.1.0 开始,常量可以声明为 final。

示例 #10 简单类继承

<?php
ExtendClass 扩展 SimpleClass
{
// 重新定义父方法
函数 displayVar()
{
回显
"扩展类\n";
parent::displayVar();
}
}

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

上面的示例将输出

Extending class
a default value

签名兼容性规则

覆盖方法时,其签名必须与父方法兼容。否则,将发出致命错误,或者在 PHP 8.0.0 之前,会生成 E_WARNING 级别错误。如果签名符合 变体 规则、将必选参数设为可选、仅添加可选的新参数以及不限制而仅放宽可见性,则签名是兼容的。这被称为里氏替换原则,简称 LSP。 构造函数private 方法不受这些签名兼容性规则的约束,因此在签名不匹配的情况下不会发出致命错误。

示例 #11 兼容的子方法

<?php

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

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

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

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

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

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

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
警告

在子类中重命名方法的参数不构成签名不兼容。但是,不建议这样做,因为如果使用命名参数,这将导致运行时 Error

示例 #14 在使用命名参数且参数在子类中被重命名时发生的错误

<?php

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

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
命名空间 NS {
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
命名空间 NS {
ClassName {
}
}
$c = new ClassName();
print
$c::class;
?>

上面的示例将输出

NS\ClassName

空安全方法和属性

从 PHP 8.0.0 开始,也可以使用“空安全”运算符访问属性和方法:?->。空安全运算符的工作方式与上述属性或方法访问相同,只是如果被取消引用的对象为 null,则将返回 null,而不是抛出异常。如果取消引用是链的一部分,则会跳过链的其余部分。

其效果类似于首先包装每个访问以进行 is_null() 检查,但更紧凑。

示例 #18 空安全运算符

<?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 被认为是属性或方法返回值的有效且预期的可能值时,最好使用空安全运算符。为了指示错误,最好抛出异常。

添加注释

用户贡献的注释 11 条注释

641
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 来更改对象实例的状态,这些更改也会在 $assignment 下显示,因为它指向同一个对象实例。

<?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 是一个不同的数据槽,它仍然会保存其指向对象实例的句柄副本,因此它不会为 NULL。

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

//
// $objectVar --->+---------+
// | NULL |
// $reference --->+---------+
//
// +---------+
// $assignment -->|(handle1)----+
// +---------+ |
// |
// v
// Object(1):foo="qux"
?>
88
kStarbe at gmail point com
7 年前
你在第二个例子中开始使用 ::,尽管还没有解释静态的概念。当你从基础开始学习时,这一点不容易发现。
129
Doug
14 年前
$thisself 之间有什么区别?

在类定义内部,$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
*/
?>
25
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 ПАСХА(); // 未捕获的警告。
?>
70
wbcarts at juno dot com
16 年前
表示“理想世界”的类和对象

如果可以通过说 $son->mowLawn() 来修剪草坪,那不是很棒吗?假设函数 mowLawn() 已定义,并且你有一个不会抛出错误的儿子,那么草坪就会被修剪。

在以下示例中,让 Line3D 类型的对象在三维空间中测量自己的长度。当类本身包含所有必要数据并具备进行计算的能力时,为什么我或 PHP 必须从类外部提供另一种方法来计算长度呢?

<?php

/*
* Point3D.php
*
* 表示三维空间中的一个位置或点
* 使用 (x, y, z) 坐标系。
*/
class Point3D
{
public
$x;
public
$y;
public
$z; // 此点的 x 坐标。

/*
* 使用从 Point.php 继承的 x 和 y 变量。
*/
public function __construct($xCoord=0, $yCoord=0, $zCoord=0)
{
$this->x = $xCoord;
$this->y = $yCoord;
$this->z = $zCoord;
}

/*
* 此点的 (字符串) 表示形式为 "Point3D(x, y, z)"。
*/
public function __toString()
{
return
'Point3D(x=' . $this->x . ', y=' . $this->y . ', z=' . $this->z . ')';
}
}

/*
* Line3D.php
*
* 使用两个 Point3D 对象表示三维空间中的一条线。
*/
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);
}

/*
* 计算此线在三维空间中的长度。
*/
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)
);
}

/*
* 此线的 (字符串) 表示形式为 "Line3D[start, end, length]"。
*/
public function __toString()
{
return
'Line3D[start=' . $this->start .
', end=' . $this->end .
', length=' . $this->getLength() . ']';
}
}

/*
* 创建并显示 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]

我对 OOP 最喜欢的一点是,“好的”对象会自行约束。我的意思是,现实中也是完全一样的……比如,如果你雇了一个水管工来修理厨房的水槽,你难道不希望他找出最佳的解决方法吗?他难道不会讨厌你想要控制整个工作过程吗?你难道不希望他不会给你带来额外的麻烦吗?而且,天哪,要求他在离开之前打扫干净是不是太过分了?

我说,好好设计你的类,以便它们能够不受干扰地完成自己的工作……谁喜欢坏消息?而且,如果你的类和对象定义良好、经过深思熟虑,并且拥有所有必要的数据来处理(就像上面的例子一样),你就不需要从类的外部微观管理整个程序。换句话说……创建一个对象,然后放手让它运行!
30
moty66 at gmail dot com
15 年前
我希望这将有助于理解如何在类内部使用静态变量

<?php

class 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
5
pawel dot zimnowodzki at gmail dot com
2 年前
虽然对于不存在的数组键没有空安全运算符,但我找到了解决方法:($array['not_existed_key'] ?? null)?->methodName()
38
关于 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 上测试)
3
johannes dot kingma at gmail dot com
3 年前
注意!

就像 Hayley Watson 指出的那样,类名不区分大小写。

<?php
class Foo{}
class
foo{} // Fatal error: Cannot declare class foo, because the name is already in use
?>
以及
<?php
class BAR{}
$bar = new Bar();
echo
get_class($bar);
?>

是完全可以的,并且将返回“BAR”。

但这对类自动加载有影响。标准的 spl_autoload 函数会将类名转换为小写以应对不区分大小写的情况,因此只有当文件名是 bar.php(或如果使用 spl_autoload_extensions(); 注册了扩展名,则为其他变体)时,才能找到类 BAR,而不是对于区分大小写的文件和操作系统(如 Linux)的 BAR.php。Windows 文件系统区分大小写,但操作系统不区分大小写,因此自动加载 BAR.php 将有效。
15
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; // 此类的相关数据...

// 用于计算总价的函数
// 用于格式化数字的函数
// 用于处理用户输入/验证的函数
// 用于构建输出字符串的函数
// 用于写入数据库的函数
// 用于读取数据库的函数
// 等等
}
?>

想象一下,您编写的每个函数只调用该类中的数据片段。某些函数可能会访问所有数据,而其他函数可能只访问一个数据片段。如果每个函数都围绕内部数据展开,那么您就创建了一个良好的类。
3
Anonymous
6 年前
起初,我对赋值与引用也感到困惑,但最终我弄明白了。这是一个与其中一条评论有些类似的其他示例,但对那些没有理解第一个示例的人可能有所帮助。将对象实例想象成房间,您可以在其中存储和操作属性和函数。包含对象的变量只是保存到该房间的“密钥”,从而访问对象。当您将此变量赋值给另一个新变量时,您所做的是复制该密钥并将其提供给这个新变量。这意味着这两个变量现在可以访问同一个“房间”(对象),并且可以进入并操作值。但是,当您创建引用时,您所做的是使变量共享同一个密钥。它们都可以访问该房间。如果其中一个变量被赋予了一个新的密钥,那么它们共享的密钥就会被替换,并且它们现在共享一个新的不同密钥。这不会影响具有旧密钥副本的其他变量……该变量仍然可以访问第一个房间。
To Top