何时在 $ this 上使用 self?

在 PHP 5 中,使用self$this什么区别?

什么时候合适?

答案

简短答案

使用$this引用当前对象。使用self来引用当前类。换句话说,对非静态成员使用$this->member ,对静态成员使用self::$member

完整答案

这是对非静态和静态成员变量正确使用$thisself的示例:

<?php
class X {
    private $non_static_member = 1;
    private static $static_member = 2;

    function __construct() {
        echo $this->non_static_member . ' '
           . self::$static_member;
    }
}

new X();
?>

这是对非静态和静态成员变量错误使用$thisself的示例:

<?php
class X {
    private $non_static_member = 1;
    private static $static_member = 2;

    function __construct() {
        echo self::$non_static_member . ' '
           . $this->static_member;
    }
}

new X();
?>

这是成员函数带有$this多态示例:

<?php
class X {
    function foo() {
        echo 'X::foo()';
    }

    function bar() {
        $this->foo();
    }
}

class Y extends X {
    function foo() {
        echo 'Y::foo()';
    }
}

$x = new Y();
$x->bar();
?>

这是通过将self用作成员函数来抑制多态行为的示例:

<?php
class X {
    function foo() {
        echo 'X::foo()';
    }

    function bar() {
        self::foo();
    }
}

class Y extends X {
    function foo() {
        echo 'Y::foo()';
    }
}

$x = new Y();
$x->bar();
?>

这个想法是$this->foo()调用foo()成员函数,而不管当前对象的确切类型是什么。如果对象的type X ,则它将调用X::foo() 。如果对象的type Y ,则调用Y::foo() 。但是对于 self :: foo(),总是会调用X::foo()

http://www.phpbuilder.com/board/showthread.php?t=10354489

通过http://board.phpbuilder.com/member.php?145249-laserlight

关键字自不仅仅指的是 “当前类的,至少不会在某种程度上,限制你的静态成员。在非静态成员的上下文中, self还提供了一种绕过当前对象的 vtable 的方法( 请参阅 vtable 上的 wiki )。正如您可以使用parent::methodName()调用函数的父版本一样,您可以调用self::methodName()来调用方法的当前类实现。

class Person {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }

    public function getTitle() {
        return $this->getName()." the person";
    }

    public function sayHello() {
        echo "Hello, I'm ".$this->getTitle()."<br/>";
    }

    public function sayGoodbye() {
        echo "Goodbye from ".self::getTitle()."<br/>";
    }
}

class Geek extends Person {
    public function __construct($name) {
        parent::__construct($name);
    }

    public function getTitle() {
        return $this->getName()." the geek";
    }
}

$geekObj = new Geek("Ludwig");
$geekObj->sayHello();
$geekObj->sayGoodbye();

这将输出:

您好,我是极客路德维希
告别路德维希的人

sayHello()使用$this指针,因此调用了 vtable 来调用Geek::getTitle()sayGoodbye()使用self::getTitle() ,因此不使用 vtable,并且调用Person::getTitle() 。在这两种情况下,我们都处理实例化对象的方法,并且可以访问被调用函数中的$this指针。

不要使用self:: ,请使用static::

自我的另一方面::值得一提。烦人的self::指的是定义时的范围而不是执行时的范围 。考虑使用两种方法的简单类:

class Person
{

    public static function status()
    {
        self::getStatus();
    }

    protected static function getStatus()
    {
        echo "Person is alive";
    }

}

如果调用Person::status()我们将看到 “Person is alive”。现在考虑当我们创建一个继承自此的类时会发生什么:

class Deceased extends Person
{

    protected static function getStatus()
    {
        echo "Person is deceased";
    }

}

调用Deceased::status()我们希望看到 “Person 已死”,但是我们看到的是 “Person 仍然活着”,因为在定义对self::getStatus()调用时,范围包含原始方法定义。

PHP 5.3 有一个解决方案。 static:: resolution 运算符实现 “后期静态绑定”,这是一种将其绑定到所调用类的范围的奇特方法。将status()的行更改为static::getStatus() ,结果就是您所期望的。在较旧版本的 PHP 中,您将必须找到解决方法。

参见PHP 文档

所以回答问题不是按要求...

$this->引用当前对象(类的实例),而static::引用类

要真正了解我们在谈论self$this时在谈论什么,我们需要在概念和实践层面上深入研究正在发生的事情。我真的没有任何答案可以适当地做到这一点,所以这是我的尝试。

让我们开始讨论什么是对象

概念上的类和对象

那么,什么 课程 ?许多人将其定义为对象的蓝图模板 。实际上,您可以在此处阅读有关PHP 中的类的更多信息。在某种程度上,这就是真正的含义。让我们看一课:

class Person {
    public $name = 'my name';
    public function sayHello() {
        echo "Hello";
    }
}

如您所知,该类上有一个名为$name的属性和一个名为sayHello()的方法(函数sayHello()

值得注意的是, 是一个静态结构,这是非常重要的。这意味着,一旦定义了Person类,无论您在哪里查看,它始终是相同的。

另一方面,对象就是所谓的类的实例 。这意味着我们采用了该类的 “蓝图”,并使用它来创建动态副本。现在,此副本已专门与其存储在其中的变量绑定。因此,对实例的任何更改都是该实例的本地更改。

$bob = new Person;
$adam = new Person;
$bob->name = 'Bob';
echo $adam->name; // "my name"

我们使用new运算符创建类的新实例

因此,我们说一个类是一个全局结构,一个对象是一个局部结构。不用担心这种有趣的->语法,我们将对此进行一些介绍。

我们应该讨论的另一件事是,我们可以检查实例是否是特定类的instanceof$bob instanceof Person如果$bob实例是使用Person Person的子对象制成的,则返回一个布尔值。

定义状态

因此,让我们深入探讨一个类实际包含的内容。一个类包含 5 种类型的 “事物”:

  1. 属性 - 将它们视为每个实例将包含的变量。

    class Foo {
        public $bar = 1;
    }
  2. 静态属性 - 将它们视为在类级别共享的变量。意味着它们永远不会被每个实例复制。

    class Foo {
        public static $bar = 1;
    }
  3. 方法 - 这些是每个实例将包含的功能(并对实例进行操作)。

    class Foo {
        public function bar() {}
    }
  4. 静态方法 - 这些是在整个类中共享的函数。它们不对实例进行操作,而是仅对静态属性进行操作。

    class Foo {
        public static function bar() {}
    }
  5. 常量 - 类解析的常量。这里不做更深入的介绍,但是为了完整性起见:

    class Foo {
        const BAR = 1;
    }

因此,基本上,我们使用关于静态的 “提示” 将信息存储在类和对象容器上,这些 “提示” 标识信息是否共享(因此是静态)(是否是共享)(因此是动态)。

状态与方法

在方法内部,对象的实例由$this变量表示。该对象的当前状态在那里,并且更改(更改)任何属性都将导致该实例(但其他实例)不发生更改。

如果静态调用方法, 则不会定义 $this变量。这是因为没有实例与静态调用关联。

这里有趣的是如何进行静态调用。因此,让我们谈谈如何访问状态:

进入国

因此,既然我们已经存储了该状态,就需要访问它。这能有点棘手(或方式多一点),所以让我们拆到这两种观点:从一个实例 / 类以外的(说从一个普通的函数调用,或从全球范围),以及一个实例内部 / class(从对象的方法内部)。

从实例 / 类的外部

从实例 / 类的外部,我们的规则非常简单且可预测。我们有两个运算符,每个运算符都会立即告诉我们是否要处理实例或静态类:

  • -> - object-operator-在访问实例时始终使用它。

    $bob = new Person;
    echo $bob->name;

    重要的是要注意,调用Person->foo没有任何意义(因为Person是一个类,而不是实例)。因此,这是一个解析错误。

  • :: - scope-resolution-operator-始终用于访问 Class 静态属性或方法。

    echo Foo::bar()

    另外,我们可以用相同的方式在对象上调用静态方法:

    echo $foo::bar()

    极其重要的是要注意,当我们从外部执行此操作 ,对象的实例从bar()方法中隐藏了。这意味着它与运行完全相同:

    $class = get_class($foo);
    $class::bar();

因此,在静态调用中未定义$this

从实例 / 类内部

这里的情况有所变化。使用相同的运算符,但是它们的含义变得明显模糊。

对象操作符 ->仍用于调用对象的实例状态。

class Foo {
    public $a = 1;
    public function bar() {
        return $this->a;
    }
}

使用对象操作符在$fooFoo一个实例bar()上调用bar()方法: $foo->bar()将产生实例的$a版本。

这就是我们的期望。

::运算符的含义虽然有所变化。这取决于对当前函数的调用上下文:

  • 在静态范围内

    在静态上下文中,使用::任何调用也将是静态的。让我们看一个例子:

    class Foo {
        public function bar() {
            return Foo::baz();
        }
        public function baz() {
            return isset($this);
        }
    }

    调用Foo::bar()将静态调用baz()方法,因此不会填充$this 。值得注意的是,在最新版本的 PHP(5.3+)中,这将触发E_STRICT错误,因为我们正在静态地调用非静态方法。

  • 在实例上下文中

    另一方面,在实例上下文中,使用::进行的调用取决于调用的接收者(我们正在调用的方法)。如果该方法定义为static ,则它将使用静态调用。如果不是,它将转发实例信息。

    因此,查看上面的代码,调用$foo->bar()将返回true ,因为 “静态” 调用发生在实例上下文内部。

合理?不这么认为。令人困惑。

快捷关键字

由于使用类名将所有内容捆绑在一起非常麻烦,因此 PHP 提供了 3 个基本的 “捷径” 关键字来简化范围解析。

  • self引用当前的类名。因此self::baz()Foo类中的Foo::baz()相同(任何方法)。

  • parent指当前类的父级。

  • static - 这是指被调用的类。由于继承,子类可以覆盖方法和静态属性。因此,使用static而不是类名来调用它们可以使我们确定调用的来源而不是当前级别。

例子

理解这一点的最简单方法是开始看一些示例。让我们选择一个类:

class Person {
    public static $number = 0;
    public $id = 0;
    public function __construct() {
        self::$number++;
        $this->id = self::$number;
    }
    public $name = "";
    public function getName() {
        return $this->name;
    }
    public function getId() {
        return $this->id;
    }
}

class Child extends Person {
    public $age = 0;
    public function __construct($age) {
        $this->age = $age;
        parent::__construct();
    }
    public function getName() {
        return 'child: ' . parent::getName();
    }
}

现在,我们还在这里研究继承。暂时忽略这是一个不好的对象模型,但是让我们看一下使用此对象会发生什么:

$bob = new Person;
$bob->name = "Bob";
$adam = new Person;
$adam->name = "Adam";
$billy = new Child;
$billy->name = "Billy";
var_dump($bob->getId()); // 1
var_dump($adam->getId()); // 2
var_dump($billy->getId()); // 3

因此,ID 计数器在实例和子实例之间共享(因为我们使用self来访问它。如果使用static ,则可以在子类中覆盖它)。

var_dump($bob->getName()); // Bob
var_dump($adam->getName()); // Adam
var_dump($billy->getName()); // child: Billy

请注意,我们每次都执行Person::getName() 实例方法。但是在一种情况下(子情况下),我们使用parent::getName()来做到这一点。这就是使此方法功能强大的原因。

警告语#1

注意,调用上下文是确定是否使用实例的因素。因此:

class Foo {
    public function isFoo() {
        return $this instanceof Foo;
    }
}

并非总是如此。

class Bar {
    public function doSomething() {
        return Foo::isFoo();
    }
}
$b = new Bar;
var_dump($b->doSomething()); // bool(false)

现在,这里真的很奇怪。我们正在调用另一个类,但是传递给Foo::isFoo()方法的$this$bar的实例。

这可能会导致各种错误和概念上的 WTF-ery。因此,我强烈建议避免在实例方法中的::运算符上使用除这三个虚拟 “快捷” 关键字( staticselfparent )以外的任何方法。

警示语#2

请注意,静态方法和属性由所有人共享。这使得它们基本上是全局变量。与全局变量一起带来的所有相同问题。因此,除非您对真正的全局性感到满意,否则我真的很犹豫将信息存储在静态方法 / 属性中。

警告语#3

通常,您需要通过使用static而不是self来使用所谓的 Late-Static-Binding。但是请注意,它们不是一回事,所以说 “总是使用static而不是self是很短视的。相反,停下来想一想您要进行的调用,并考虑是否希望子类能够覆盖该调用静态解决的呼叫。

TL / DR

太糟糕了,请回头阅读。可能太长了,但是太长了,因为这是一个复杂的主题

TL / DR#2

好的。简而言之, self用于引用一个类中的当前类名 ,其中$this表示当前对象实例 。请注意, self是复制 / 粘贴的快捷方式。您可以使用您的班级名称安全地替换它,它将正常运行。但是$this是一个动态变量,无法提前确定(甚至可能不是您的课程)。

TL / DR#3

如果使用对象运算符( -> ),那么您始终知道您正在处理实例。如果使用 scope-resolution-operator( :: ,则需要有关上下文的更多信息(我们是否已在对象上下文中?我们是否在对象外部?等等)。

self (不是 $ self)是指类的类型 ,其中$this是指类的当前实例self用于静态成员函数,以允许您访问静态成员变量。 $this在非静态成员函数中使用,是对调用成员函数的类的实例的引用。

因为this是一个对象,所以您可以像这样使用它: $this->member

因为self不是对象,所以它基本上是一种自动引用当前类的类型,您可以这样使用它: self::member

$this->用于引用类变量(成员变量)或方法的特定实例。

Example: 
$derek = new Person();

$ derek 现在是 Person 的特定实例。每个人都有一个名字和一个姓氏,但是 $ derek 有一个特定的名字和姓氏(Derek Martin)。在 $ derek 实例中,我们可以将它们称为 $ this-> first_name 和 $ this-> last_name

ClassName :: 用于指代该类型的类及其静态变量,静态方法。如果有帮助,您可以在脑海中将 “静态” 一词替换为 “共享”。因为它们是共享的,所以它们不能引用 $ this,后者引用的是特定实例(未共享)。静态变量(即静态 $ db_connection)可以在一种对象的所有实例之间共享。例如,所有数据库对象共享一个连接(静态 $ connection)。

静态变量示例:假设我们有一个带有单个成员变量的数据库类:static $ num_connections; 现在,将其放入构造函数中:

function __construct()
{
    if(!isset $num_connections || $num_connections==null)
    {
        $num_connections=0;
    }
    else
    {
        $num_connections++;
    }
}

就像对象具有构造函数一样,它们也具有析构函数,这些析构函数在对象死亡或未设置时执行:

function __destruct()
{
    $num_connections--;
}

每次创建新实例时,连接计数器都会增加一个。每次我们销毁或停止使用实例时,连接计数器都会减少一。通过这种方式,我们可以监视正在使用的数据库对象实例的数量:

echo DB::num_connections;

由于 $ num_connections 是静态的(共享的),它将反映活动数据库对象的总数。您可能已经看到了用于在数据库类的所有实例之间共享数据库连接的这项技术。这样做是因为创建数据库连接需要很长时间,因此最好仅创建一个并共享它(这称为 “单例模式”)。

可以使用静态方法(即公共静态 View :: format_phone_number($ digits)),而无需首先实例化这些对象之一(即,它们在内部未引用 $ this)。

静态方法示例:

public static function prettyName($first_name, $last_name)
{
    echo ucfirst($first_name).' '.ucfirst($last_name);
}

echo Person::prettyName($derek->first_name, $derek->last_name);

如您所见,公共静态函数 prettyName 对对象一无所知。它只是与您传入的参数一起使用,就像不属于对象的普通函数一样。那么,如果我们不把它作为对象的一部分,那又何必呢?

  1. 首先,将功能附加到对象可以帮助您使事物保持井井有条,因此您知道在哪里可以找到它们。
  2. 其次,它可以防止命名冲突。在一个大项目中,您可能有两个开发人员创建 getName()函数。如果一个创建了 ClassName1 :: getName(),而另一个创建了 ClassName2 :: getName(),则完全没有问题。没有冲突。耶静态方法!

SELF ::如果您在具有要引用的静态方法的对象之外进行编码,则必须使用对象的名称 View :: format_phone_number($ phone_number); 对其进行调用。如果具有要引用的静态方法的对象进行编码,则可以使用对象的名称 View :: format_phone_number($ pn),也可以使用 self :: format_phone_number($ pn)快捷方式

静态变量也是如此: 示例: View :: templates_path 与 self :: templates_path

在 DB 类内部,如果我们引用其他对象的静态方法,则将使用该对象的名称: 示例: Session :: getUsersOnline();

但是,如果 DB 类想要引用其自己的静态变量,则只会说 self: 示例: self :: connection;

希望可以帮助解决问题:)

这篇博客文章

  • self是指当前阶层
  • self可以用来调用静态函数并引用静态成员变量
  • self可以在静态函数中使用
  • self也可以通过绕过 vtable 来关闭多态行为
  • $this指向当前对象
  • $this可用于调用静态函数
  • $this不应用于调用静态成员变量。改用self
  • $this不能在静态函数中使用

在 PHP 中,您可以使用 self 关键字来访问静态属性和方法。

问题是您可以在任何地方用self::method()替换$this->method() ,无论method()是否声明为静态。那么您应该使用哪一个呢?

考虑以下代码:

class ParentClass {
    function test() {
        self::who();    // will output 'parent'
        $this->who();   // will output 'child'
    }

    function who() {
        echo 'parent';
    }
}

class ChildClass extends ParentClass {
    function who() {
        echo 'child';
    }
}

$obj = new ChildClass();
$obj->test();

在此示例中, self::who()将始终输出 “parent”,而$this->who()将取决于对象具有的类。

现在我们可以看到 self 指向调用它的类,而$this指向当前对象

因此,仅在$this不可用时,或不想让子孙类覆盖当前方法时,才应使用 self。

在类定义中, $this引用当前对象,而self引用当前类。

有必要使用self来引用类元素,并使用$this来引用对象元素。

self::STAT // refer to a constant value
self::$stat // static variable
$this->stat // refer to an object variable

根据http://www.php.net/manual/en/language.oop5.static.php ,没有$self 。只有$this用来引用该类(对象)的当前实例,而 self 可以用来引用一个类的静态成员。对象实例和类之间的区别在这里起作用。