提问



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


什么时候适合?

最佳参考


简答




  使用$this表示当前
  目的。使用self来指代
  现在的课程。换句话说,使用
  $this->member对于非静态成员,
  将self::$member用于静态成员。



完整答案



以下是对非静态和静态成员变量的[[strong>正确 $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();
?>


以下是成员函数的[[strong>多态与$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:[206]


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

其它参考1


关键字self NOT 仅指当前类,至少不会限制您使用静态成员。在非静态成员的上下文中,self还提供了绕过当前对象的vtable(参见vtable on vtable)的方法。正如您可以使用parent::methodName()来调用函数的父版本,因此您可以调用self::methodName()来调用方法的当前类实现。[208]


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指针。

其它参考2


请勿使用self::,请使用static::


自我的另一个方面::值得一提。恼人地 self::指的是定义点而不是执行点的范围。考虑这个简单的类有两种方法:


class Person
{

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

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

}


如果我们打电话Person::status(),我们会看到人还活着。现在考虑当我们创建一个继承自this的类时会发生什么:


class Deceased extends Person
{

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

}


调用Deceased::status()我们希望看到人死了,但我们看到的是人还活着,因为范围包含定义调用self::getStatus()时的原始方法定义。


PHP 5.3有一个解决方案。 static:: 解析运算符实现了后期静态绑定,这是一种说法它被绑定到被调用类的范围的奇特方式。更改status()中的行到static::getStatus(),结果是你所期望的。在旧版本的PHP中,你必须找到一个kludge来做到这一点。


请参阅PHP文档[209]


所以回答问题不是问...


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

其它参考3


为了真正理解我们在谈论self$this时所谈论的内容,我们需要真正深入研究在概念和实践层面上发生的事情。我不觉得任何答案都适当地做到了,所以这是我的尝试。


让我们首先谈谈类和对象是什么。


类和对象,从概念上讲



那么, 类是什么?很多人将其定义为对象的蓝图或模板。实际上,您可以在此处阅读有关PHP中的类的更多信息。并且在某种程度上说它是真实的。让我们来看一个类:


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


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


重要的是要注意类是一个静态结构。这意味着一旦定义了类Person,你看到的地方总是一样的在它。


另一方面,对象是一个叫做类的实例的东西。这意味着我们采用类的蓝图,并用它来制作动态副本。现在特别绑定到它存储的变量。因此,对实例的任何更改都是该实例的本地更改。


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


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


因此,我们说Class是一个全局结构,Object是一个本地结构。不要担心那种有趣的->语法,我们会在一点点进入。


我们应该讨论的另一件事是,如果一个实例是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;
    }
    



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


州和方法



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


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


这里有趣的是如何进行静态调用。那么让我们谈谈我们如何访问该州:


访问状态



所以现在我们已经存储了那个状态,我们需要访问它。这可能会有点棘手(或方式不止一点),所以让我们将它分成两个视点:来自实例/类的外部(比如来自正常的函数调用,或来自全局范围),以及实例/类的内部(来自对象上的方法)。


来自实例/类的外部



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



  • -> - 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


来自实例/类的内部



事情在这里有所改变。使用相同的运算符,但它们的含义变得非常模糊。


object-operator ->仍然用于调用对象的实例状态。


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


使用对象运算符$foo->bar()调用$foo(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



如果使用了对象运算符(->),那么总是知道你正在处理一个实例。如果使用范围解析运算符(::) ,您需要有关上下文的更多信息(我们是否已经在对象上下文中?我们是否在对象之外?等等)。

其它参考4


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


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


因为self不是一个对象,它基本上是一个自动引用当前类的类型,你可以使用它:self::member

其它参考5


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


Example: 
$derek = new Person();


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


ClassName ::用于引用该类型的类及其静态变量,静态方法。如果有帮助,您可以在心理上将静态替换为共享。因为它们是共享的,所以它们不能引用$ this,它引用特定的实例(不共享)。静态变量(即静态$ db_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是静态的(共享),所以它将反映Activity数据库对象的总数。您可能已经看到过这种技术用于在数据库类的所有实例之间共享数据库连接。这样做是因为创建数据库连接需要很长时间,因此最好只创建一个并共享它(这称为单例模式)。


静态方法(即公共静态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);


如您所见,public static function prettyName对该对象一无所知。它只是处理你传入的参数,就像一个不属于对象的普通函数。那么,为什么我们可以把它作为对象的一部分呢?



  1. 首先,将功能附加到对象可以帮助您保持组织有序,因此您知道在哪里找到它们。

  2. 其次,它可以防止命名冲突。在一个大项目中,你可能有两个开发人员创建getName()函数。如果一个创建一个ClassName1 :: getName(),另一个创建ClassName2 :: getName(),它根本没问题。没有冲突。 Yay静态方法!



SELF ::
如果您在外部编码具有要引用的静态方法的对象,则必须使用对象的名称View :: format_phone_number($ phone_number)调用它;
如果您在内部编码具有您想要引用的静态方法的对象,则可以 使用对象的名称View :: format_phone_number($ pn),或者您可以使用self :: format_phone_number($ pn)快捷方式


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


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


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


希望有助于清理事情:)

其它参考6


来自这篇博文:[211]



  

      
  • self指当前的班级

  •   
  • self可用于调用静态函数和引用静态成员变量

  •   
  • self可以在静态函数中使用

  •   
  • self也可以绕过vtable
  • 来关闭多态行为
      
  • $this指当前对象

  •   
  • $this可用于调用静态函数

  •   
  • $this不应该用于调用静态成员变量。请改用self

  •   
  • $this不能在静态函数中使用

  •   


其它参考7


在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。

其它参考8


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


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


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

其它参考9



  这是一个正确使用$ this和self用于非静态的示例
  和静态成员变量:



<?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();
?> 

其它参考10


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

其它参考11


由于这里没有人谈到表演,这里有一个小基准(5.6):


 Name     | Time    | Percent  
----------|---------|---------  
 $this->  | 0.99163 | 106.23%  
 self::   | 0.96912 | 103.82%  
 static:: | 0.93348 | 100%


这些是2 000 000次运行的结果,这是我使用的代码:


<?php

require '../vendor/autoload.php';

// My small class to do benchmarks
// All it does is looping over every test x times and record the
//   time it takes using `microtime(true)`
// Then, the percentage is calculated, with 100% being the quickest
// Times are being rouned for outputting only, not to calculate the percentages
$b = new Tleb\Benchmark\Benchmark(2000000);

class Foo
{
    public function calling_this()
    {
        $this->called();
    }

    public function calling_self()
    {
        self::called();
    }

    public function calling_static()
    {
        static::called();
    }

    public static function called()
    {
    }
}

$b->add('$this->',  function () { $foo = new Foo; $foo->calling_this(); });
$b->add('self::',   function () { $foo = new Foo; $foo->calling_self(); });
$b->add('static::', function () { $foo = new Foo; $foo->calling_static(); });

$b->run();

其它参考12


self指的是当前的类(在其中被称为),


$this指当前对象。
你可以使用static而不是self。
看例子:


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

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

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

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


输出:
      亲
     子

其它参考13


我相信问题不在于您是否可以通过调用ClassName::staticMember来调用类的静态成员。问题是使用self::classmember$this->classmember之间的区别。


例如,以下两个示例均可正常运行,无论您使用self::还是$this->


class Person{
    private $name;
    private $address;

    public function __construct($new_name,$new_address){
        $this->name = $new_name;
        $this->address = $new_address;
    }
}

class Person{
    private $name;
    private $address;
    public function __construct($new_name,$new_address){
        self::$name = $new_name;
        self::$address = $new_address;
    }
}

其它参考14



  • 对象指针$ this指向当前对象。

  • 类值static指的是当前对象。

  • 类值self指的是它所定义的确切类。

  • 类值parent指的是它所定义的确切类的父级。

请参阅以下显示重载的示例。


<?php

class A {

    public static function newStaticClass()
    {
        return new static;
    }

    public static function newSelfClass()
    {
        return new self;
    }

    public function newThisClass()
    {
        return new $this;
    }
}

class B extends A
{
    public function newParentClass()
    {
        return new parent;
    }
}


$b = new B;

var_dump($b::newStaticClass()); // B
var_dump($b::newSelfClass()); // A because self belongs to "A"
var_dump($b->newThisClass()); // B
var_dump($b->newParentClass()); // A


class C extends B
{
    public static function newSelfClass()
    {
        return new self;
    }
}


$c = new C;

var_dump($c::newStaticClass()); // C
var_dump($c::newSelfClass()); // C because self now points to "C" class
var_dump($c->newThisClass()); // C
var_dump($b->newParentClass()); // A because parent was defined *way back* in class "B"


大多数时候你想引用当前的类,这就是你使用static$this的原因。但是,有些时候你需要 self因为你想要原始类而不管扩展它的内容。 (很少,很少)

其它参考15


::::运算符一起使用时,它引用当前类,可以在静态和非静态上下文中完成。 $this指的是物体本身。此外,使用$this调用静态方法(但不是引用字段)是完全合法的。

其它参考16


$this引用当前类对象,self引用当前类(Not object)。该类是对象的蓝图。所以你定义了一个类,但是你构造了对象。


换句话说,使用self for staticthis for none-static members or methods


同样在子/父场景中self / parent主要用于识别子类和父类成员和方法。

其它参考17


此外,因为$this::尚未讨论过。


仅供参考,从PHP 5.3开始处理实例化对象以获取当前作用域值时,与使用static::相反,可以选择使用$this::


http://ideone.com/7etRHy[213]


class Foo
{
    const NAME = 'Foo';

    //Always Foo::NAME (Foo) due to self
    protected static $staticName = self::NAME;

    public function __construct()
    {
        echo $this::NAME;
    }

    public function getStaticName()
    {
       echo $this::$staticName;
    }
}

class Bar extends Foo
{
    const NAME = 'FooBar';

    /**
     * override getStaticName to output Bar::NAME
     */
    public function getStaticName()
    {
        $this::$staticName = $this::NAME;
        parent::getStaticName();
    }
}

$foo = new Foo; //outputs Foo
$bar = new Bar; //outputs FooBar
$foo->getStaticName(); //outputs Foo
$bar->getStaticName(); //outputs FooBar
$foo->getStaticName(); //outputs FooBar


使用上面的代码不是常见的或推荐的做法,而只是为了说明它的用法,并且更像是你知道吗?参考原始海报的问题。


它也代表$object::CONSTANT的用法,例如echo $foo::NAME;而不是$this::NAME;

其它参考18


如果要在不创建该类的对象/实例的情况下调用类的方法,请使用self,从而节省RAM(有时为此目的使用self)。换句话说,它实际上是静态调用方法。使用this作为对象透视。[214]

其它参考19


情况1:使用self可用于类常量


 class classA { 
     const FIXED_NUMBER = 4; 
     self::POUNDS_TO_KILOGRAMS
}


如果要在类之外调用它,请使用classA::POUNDS_TO_KILOGRAMS访问常量


案例2:对于静态属性


class classC {
     public function __construct() { 
     self::$_counter++; $this->num = self::$_counter;
   }
}

其它参考20


我遇到了同样的问题,简单的答案是:



  • $ this 需要类
  • 的实例
  • self :: 不会



每当您使用静态方法静态属性并想要在没有实例化类的对象的情况下调用它们时,您需要使用 self :: 要调用它们,因为 $ this 始终要求创建对象。

其它参考21


根据php.net,在这种情况下有三个特殊的关键词:selfparentstatic。它们用于从类定义中访问属性或方法。


另一方面,$this用于调用任何类的实例和方法,只要该类可访问即可。