关于PHP:公共、私有和受保护之间有什么区别?

What is the difference between public, private, and protected?

什么时候以及为什么要在类中使用publicprivateprotected函数和变量?他们之间有什么区别?

实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Public
public $variable;
public function doSomething() {
  // ...
}

// Private
private $variable;
private function doSomething() {
  // ...
}

// Protected
protected $variable;
protected function doSomething() {
  // ...
}


您使用:

  • public作用域,使该变量/函数可从对象的任何地方、其他类和实例中使用。

  • 当您希望变量/函数仅在其自己的类中可见时,private作用域。

  • 当您希望使变量/函数在扩展当前类(包括父类)的所有类中可见时,可以使用protected作用域。

更多:(综合信息)

  • PHP手册-可见性


dd

公众:

当您将方法(函数)或属性(变量)声明为public时,可以通过以下方式访问这些方法和属性:

  • 声明它的类。
  • 继承上述声明类的类。
  • 类之外的任何外部元素也可以访问这些内容。

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php

class GrandPa
{
    public $name='Mark Henry';  // A public variable
}

class Daddy extends GrandPa // Inherited class
{
    function displayGrandPaName()
    {
        return $this->name; // The public variable will be available to the inherited class
    }

}

// Inherited class Daddy wants to know Grandpas Name
$daddy = new Daddy;
echo $daddy->displayGrandPaName(); // Prints 'Mark Henry'

// Public variables can also be accessed outside of the class!
$outsiderWantstoKnowGrandpasName = new GrandPa;
echo $outsiderWantstoKnowGrandpasName->name; // Prints 'Mark Henry'

受保护的:

当您将方法(函数)或属性(变量)声明为protected时,可以通过

  • 声明它的类。
  • 继承上述声明类的类。

外部成员无法访问这些变量。"从某种意义上说,它们不是声明类本身的对象实例。

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php

class GrandPa
{
    protected $name = 'Mark Henry';
}

class Daddy extends GrandPa
{
    function displayGrandPaName()
    {
        return $this->name;
    }

}

$daddy = new Daddy;
echo $daddy->displayGrandPaName(); // Prints 'Mark Henry'

$outsiderWantstoKnowGrandpasName = new GrandPa;
echo $outsiderWantstoKnowGrandpasName->name; // Results in a Fatal Error

具体错误如下:

PHP Fatal error: Cannot access protected property GrandPa::$name

私人:

当您将方法(函数)或属性(变量)声明为private时,可以通过以下方式访问这些方法和属性:

  • 声明它的类。

外部成员无法访问这些变量。从某种意义上说,它们不是已声明类本身的对象实例,甚至不是继承已声明类的类的对象实例。

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php

class GrandPa
{
    private $name = 'Mark Henry';
}

class Daddy extends GrandPa
{
    function displayGrandPaName()
    {
        return $this->name;
    }

}

$daddy = new Daddy;
echo $daddy->displayGrandPaName(); // Results in a Notice

$outsiderWantstoKnowGrandpasName = new GrandPa;
echo $outsiderWantstoKnowGrandpasName->name; // Results in a Fatal Error

确切的错误信息是:

Notice: Undefined property: Daddy::$name
Fatal error: Cannot access private property GrandPa::$name

用反射法解剖外公课

这个主题并没有完全超出范围,我在这里添加它只是为了证明反射是非常强大的。正如我在上述三个示例中所述,protectedprivate成员(属性和方法)不能在类外访问。

但是,通过反射,您甚至可以访问类外的protectedprivate成员,从而实现额外的普通操作!

好吧,反射是什么?

Reflection adds the ability to reverse-engineer classes, interfaces,
functions, methods and extensions. Additionally, they offers ways to
retrieve doc comments for functions, classes and methods.

序言

我们有一个名为Grandpas的类,并说我们有三个属性。为了便于理解,请考虑到有三位祖父的名字:

  • 马克亨利
  • 约翰冲突
  • 威尔琼斯

让我们分别将它们(赋值修饰符)设为publicprotectedprivate。你很清楚,protectedprivate成员不能在班外访问。现在让我们用反省来反驳这个说法。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<?php

class GrandPas   // The Grandfather's class
{
    public     $name1 = 'Mark Henry';  // This grandpa is mapped to a public modifier
    protected  $name2 = 'John Clash';  // This grandpa is mapped to a protected  modifier
    private    $name3 = 'Will Jones';  // This grandpa is mapped to a private modifier
}


# Scenario 1: without reflection
$granpaWithoutReflection = new GrandPas;

# Normal looping to print all the members of this class
echo"#Scenario 1: Without reflection";
echo"Printing members the usual way.. (without reflection)";
foreach($granpaWithoutReflection as $k=>$v)
{
    echo"The name of grandpa is $v and he resides in the variable $k";
}

echo"";

#Scenario 2: Using reflection

$granpa = new ReflectionClass('GrandPas'); // Pass the Grandpas class as the input for the Reflection class
$granpaNames=$granpa->getDefaultProperties(); // Gets all the properties of the Grandpas class (Even though it is a protected or private)


echo"#Scenario 2: With reflection";
echo"Printing members the 'reflect' way..";

foreach($granpaNames as $k=>$v)
{
    echo"The name of grandpa is $v and he resides in the variable $k";
}

输出:

1
2
3
4
5
6
7
8
9
#Scenario 1: Without reflection
Printing members the usual way.. (Without reflection)
The name of grandpa is Mark Henry and he resides in the variable name1

#Scenario 2: With reflection
Printing members the 'reflect' way..
The name of grandpa is Mark Henry and he resides in the variable name1
The name of grandpa is John Clash and he resides in the variable name2
The name of grandpa is Will Jones and he resides in the variable name3

常见误解:

请不要与下面的示例混淆。如您所见,如果不使用反射,则无法在类外部访问privateprotected成员。

1
2
3
4
5
6
7
8
9
10
11
<?php

class GrandPas   // The Grandfather's class
{
    public     $name1 = 'Mark Henry';  // This grandpa is mapped to a public modifier
    protected  $name2 = 'John Clash';  // This grandpa is mapped to a protected modifier
    private    $name3 = 'Will Jones';  // This grandpa is mapped to a private modifier
}

$granpaWithoutReflections = new GrandPas;
print_r($granpaWithoutReflections);

输出:

1
2
3
4
5
6
GrandPas Object
(
    [name1] => Mark Henry
    [name2:protected] => John Clash
    [name3:GrandPas:private] => Will Jones
)

调试功能

print_rvar_exportvar_dump是调试器函数。它们以人类可读的形式提供有关变量的信息。这三个函数将显示php 5对象的protectedprivate属性。将不显示静态类成员。

更多资源:

  • PHP手册-oop属性
  • PHP手册-oop可见性
  • technflirt.com-PHP类中的可见性
  • jordizle.com-公共、私有、受php 5保护


默认为所需的最低可见性通常被视为良好的实践,因为这样可以促进数据封装和良好的接口设计。在考虑成员变量和方法可见性时,请考虑成员在与其他对象交互中所扮演的角色。

如果您"编码到一个接口而不是实现",那么做可见性决策通常非常简单。通常,变量应该是私有的或受保护的,除非您有充分的理由公开它们。使用公共访问器(getter/setter)来限制和调节对类内部的访问。

将汽车作为类比,速度、档位和方向等都是私有实例变量。你不希望司机直接操纵空燃比之类的东西。相反,您将有限数量的操作公开为公共方法。汽车接口可以包括accelerate()deccelerate()/brake()setGear()turnLeft()turnRight()等方法。

驾驶员不知道也不应该关心这些动作是如何通过汽车内部来实现的,暴露这些功能可能会对驾驶员和路上的其他人造成危险。因此,设计公共接口并在该接口后面封装数据的良好实践。

这种方法还允许您修改和改进类中公共方法的实现,而不破坏接口与客户机代码的约定。例如,您可以改进accelerate()方法以提高燃油效率,但该方法的使用将保持不变;客户机代码不需要更改,但仍然可以从效率改进中获益。

编辑:由于您似乎仍在学习面向对象的概念(这比任何语言的语法都难掌握),我强烈建议您选择Matt Zandstra提供的PHP对象、模式和实践的副本。这本书首先教我如何有效地使用OOP,而不仅仅是教我语法。我早在几年前就学会了语法,但如果不理解OOP的"为什么",那就没用了。


private—只能从类内访问

protected—可以从类内和继承类中访问。

public—也可以从类外的代码访问。

这适用于函数和变量。


区别如下:

public:类的任何用户都可以直接访问公共变量或方法。

protected:受保护的变量或方法不能被类的用户访问,但可以在继承自类的子类中访问。

private:私有变量或方法只能从定义它的类内部访问。这意味着不能从扩展类的子级调用私有变量或方法。


带有抽象示例的可见性范围::便于理解

属性或方法的可见性由三个关键字(public、protected和private)中的一个的预修复声明定义。

公共:如果一个属性或方法被定义为公共的,这意味着它可以被任何可以引用对象的东西访问和操作。

  • 把公众视野看作是任何人都可以参加的"公众野餐"。

保护:当属性或方法的可见性设置为"受保护"时,成员只能在类本身和继承的继承类中访问。(继承的):一个类可以具有另一个类的所有属性和方法。

  • 把它作为一个受保护的可视范围,看作是"公司野餐",公司成员和他们的家庭成员是不允许公开的。这是最常见的范围限制。

private:当属性或方法可见性设置为private时,尽管存在任何类关系,但只有具有private成员的类才能访问这些方法和属性(在类内部)。

  • 用野餐的比喻来说,可以把野餐看成是"只允许公司成员参加的公司野餐"。不允许家庭,也不允许公众。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
/**
 * Define MyClass
 */

class MyClass
{
    public $public = 'Public';
    protected $protected = 'Protected';
    private $private = 'Private';

    function printHello()
    {
        echo $this->public;
        echo $this->protected;
        echo $this->private;
    }
}

$obj = new MyClass();
echo $obj->public; // Works
echo $obj->protected; // Fatal Error
echo $obj->private; // Fatal Error
$obj->printHello(); // Shows Public, Protected and Private


/**
 * Define MyClass2
 */

class MyClass2 extends MyClass
{
    // We can redeclare the public and protected method, but not private
    protected $protected = 'Protected2';

    function printHello()
    {
        echo $this->public;
        echo $this->protected;
        echo $this->private;
    }
}

$obj2 = new MyClass2();
echo $obj2->public; // Works
echo $obj2->private; // Undefined
echo $obj2->protected; // Fatal Error
$obj2->printHello(); // Shows Public, Protected2, Undefined

提取自:

http://php.net/manual/en/language.oop5.visibility.php


?? Here is an easy way to remember the scope of public, protected and private.

PUBLIC

  • PUBLIC作用域:对象和其他类都可以使用公共变量/函数。

protected

  • protected作用域:所有扩展当前类的类都可以使用受保护的变量/函数。
  • 不!对象无法访问此范围

private

  • private作用域:一个私有变量/函数只在定义它的当前类中可见。
  • 不!扩展当前类的类无法访问此范围。
  • 不!对象无法访问此范围。

阅读PHP手册中方法或变量的可见性。


考虑"何时":如果我不确定的话,一开始我倾向于宣布一切都是私人的。原因是,将私有方法公开通常比另一种方法容易得多。这是因为至少可以确保私有方法没有在任何地方使用,而是在类本身中使用。公共方法可能已经在任何地方使用,可能需要大量的重写。

更新:我现在使用的是默认的protected,因为我发现它对于封装来说已经足够好了,并且在扩展类时(无论如何我都尽量避免这样做),它不会妨碍我。如果我有充分的理由使用另外两个,我会的。

private方法的一个很好的理由是实现对象固有的一些东西,即使是扩展类也不应该改变(事实上的原因,除了封装之外,比如内部状态管理)。最后,仍然很容易找到通常使用protected方法的地方,所以我现在默认为protected。我承认,也许不是100%的客观"战壕"体验。


PHP手册对这个问题有很好的理解。

The visibility of a property or method can be defined by prefixing the declaration with the keywords public, protected or private. Class members declared public can be accessed everywhere. Members declared protected can be accessed only within the class itself and by inherited and parent classes. Members declared as private may only be accessed by the class that defines the member.


对于我来说,这是理解三种属性类型最有用的方法:

public:当您对直接访问和从代码中的任何地方更改的这个变量感到满意时,可以使用它。

类外部的示例用法:

1
2
3
$myObject = new MyObject()
$myObject->publicVar = 'newvalue';
$pubVar = $myObject->publicVar;

保护:当您想强制其他程序员(以及您自己)在访问和设置变量时使用类外的getter/setter时,可以使用这个(但是您应该保持一致,并且在类内也使用getter和setter)。这个或private通常是设置所有类属性的默认方式。

为什么?因为如果您在将来的某个时候(甚至可能在5分钟内)决定要处理为该属性返回的值,或者在获取/设置之前对其进行一些操作,那么您可以在不重构项目中使用过它的所有地方的情况下进行此操作。

类外部的示例用法:

1
2
3
$myObject = new MyObject()
$myObject->setProtectedVar('newvalue');
$protectedVar = $myObject->getProtectedVar();

私有:private属性与protected属性非常相似。但区别在于,如果不使用父类的getter或setter,则使其private也使子类无法访问。

因此,基本上,如果您正在为一个属性使用getter和setter(或者如果它仅由父类内部使用,并且不打算在其他任何地方访问),那么您也可以将其设置为private,以防止任何人直接使用它并引入错误。

子类(扩展myObject)内的示例用法:

1
2
$this->setPrivateVar('newvalue');
$privateVar = $this->getPrivateVar();


它们允许不同级别的封装


php中的变量分为三种不同类型:

public:此变量类型的值在所有范围内都可用,并在代码执行时调用。声明为:public $examTimeTable;

private:此类变量的值仅对其所属的类可用。private $classRoomComputers;

Protected:仅当以继承或其子类的形式授予访问权限时,才可使用此类的值。通常使用::来授予父类访问权限

protected $familyWealth;


重新提出一个旧问题,但我认为一个很好的方法是根据您定义的API来考虑这个问题。

  • PUBLIC-所有标记为public的东西都是API的一部分,任何使用类/接口/其他的人都可以使用和依赖它。

  • 不要被愚弄,这也是API的一部分!人们可以对代码进行子类化、扩展和使用任何标记为"受保护"的代码。

  • private—私有属性和方法可以随意更改。其他人都不能用这些。这些是你唯一可以改变而不做破坏性改变的东西。

或者从表面上看:

  • PUBLICprotected的任何更改都应视为重大更改。

  • 任何新的PUBLICprotected都应(至少)轻微。

  • 只有对private的任何内容进行新的/更改才能修补

因此,在维护代码方面,最好谨慎对待您制作的PUBLICprotected,因为这些是您向用户承诺的事情。


当我们在项目中使用面向对象的PHP时,我们应该遵循一些规则来在PHP中使用访问修饰符。今天我们将清楚地了解什么是访问修饰符以及如何使用它。php访问修饰符用于设置php类及其成员的访问权限,这些类是在类范围内定义的函数和变量。在PHP中,类成员有三个作用域。

  • 公众
  • 私人的
  • 受保护的
  • 现在,让我们看看下面的图片,了解访问修改器访问级别enter image description here

    现在,让我们看一下下面的列表,了解用作访问修饰符的可能的PHP关键字。

    public:-类或其用此访问修饰符定义的成员将可以从任何位置公开访问,甚至可以从类的范围之外进行访问。

    private:-具有此关键字的类成员将在类本身内被访问。我们无法从子类访问私有数据。它保护成员不受外部类访问的影响。

    protected:-与private相同,除非允许子类访问受保护的超类成员。

    现在,请参阅表了解访问修饰符读取全文php访问修饰符


    public:当您声明一个变量或方法时,它是一个默认状态,可以被任何直接指向对象的对象访问。

    protected:只能在对象和子类中访问。

    private:只能在对象中引用,不能在子类中引用。