在PHP5中,使用self和$this有什么区别?

什么时候合适?


根据静态关键字,没有任何$self。只有$this用于引用类(对象)的当前实例,self用于引用类的静态成员。对象实例和类之间的区别在这里发挥作用。


简短回答

使用$this表示当前对象使用self指代当前类别。换句话说,使用$this->非静态成员的成员,对静态成员使用self::$member。

完整答案

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

以下是非静态和静态成员变量不正确使用$this和self的示例:

<?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(()成员函数。如果对象是X类型,那么它将调用X::foo()。如果对象类型为Y,则调用Y::foo()。但是对于self::foo(),总是调用X::foo()。

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

通过http://board.phpbuilder.com/member.php?145249-激光灯


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

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

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


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

Example: 
$derek = new Person();

$derek现在是Person的一个特定实例。每个人都有一个first_name和一个last_name,但$derek有一个特定的first_name和last_name(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是静态的(共享的),所以它将反映活动数据库对象的总数。您可能已经看到这种技术用于在数据库类的所有实例之间共享数据库连接。这样做是因为创建数据库连接需要很长时间,所以最好只创建一个并共享它(这称为Singleton模式)。

静态方法(即,public Static 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对对象一无所知。它只是处理传入的参数,就像不是对象一部分的普通函数。那么,如果我们可以不把它作为对象的一部分,那又何必麻烦呢?

首先,将函数附加到对象有助于保持事物的有序性,从而知道在哪里可以找到它们。第二,它可以防止命名冲突。在一个大型项目中,您可能需要两个开发人员创建getName()函数。如果一个创建ClassName1::getName(),另一个创建了ClassName2::getName(),则完全没有问题。没有冲突。是的静态方法!

自我::如果在具有要引用的静态方法的对象外部进行编码,则必须使用对象的名称View::format_phone_number($phone_number);如果在具有要引用的静态方法的对象内部进行编码,则可以使用对象的名称View::format_phone_number($pn),也可以使用self::format_prhone_number

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

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

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


我认为问题不在于是否可以通过调用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;
    }
}

关键字self不仅仅指“当前类”,至少不会限制静态成员。在非静态成员的上下文中,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::。使用静态::*

自我还有另一个方面:值得一提。令人讨厌的是,self::是指定义时的范围,而不是执行时的范围。考虑这个具有两个方法的简单类:

class Person
{

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

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

}

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

class Deceased extends Person
{

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

}

调用死者::status(),我们会看到“Person is dead”。然而,我们看到“Person is alive”,因为在定义对self::getStatus()的调用时,作用域包含原始方法定义。

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

请参阅PHP文档

所以,要回答这个问题,而不是问。。。

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


以下是$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();
?> 

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


对象指针$this指向当前对象。类值static引用当前对象。类值self引用了它在中定义的确切类。类值父级是指它在中定义的确切类的父级。

请参见以下显示过载的示例。

<?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,从而节省RAM(有时使用self)。换句话说,它实际上是在静态地调用一个方法。将其用于对象透视。


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

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

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

从这篇博客文章:

self指当前类self可用于调用静态函数和引用静态成员变量self可以在静态函数中使用self还可以通过绕过vtable来关闭多态行为$这是指当前对象$this可用于调用静态函数$this不应用于调用静态成员变量。改用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是局部结构。不要担心那个有趣的->语法,我们稍后将对此进行深入探讨。

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

定义状态

因此,让我们深入了解一个类实际包含的内容。一个类包含5种类型的“东西”:

财产-将这些视为每个实例将包含的变量。Foo类{public$bar=1;}静态财产(Static Properties)-将这些视为在类级别共享的变量。这意味着它们不会被每个实例复制。Foo类{公共静态$bar=1;}方法-这些是每个实例将包含的函数(并对实例进行操作)。Foo类{公共函数栏(){}}静态方法-这些是在整个类中共享的函数。它们不操作实例,而只操作静态财产。Foo类{公共静态函数栏(){}}常量-类解析常量。这里不再赘述,但为了完整起见:Foo类{常量BAR=1;}

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

状态和方法

在方法内部,对象的实例由$this变量表示。该对象的当前状态就在那里,改变(改变)任何属性都会导致对该实例(而不是其他实例)的改变。

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

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

正在访问状态

现在我们已经存储了该状态,我们需要访问它。这可能有点棘手(或者有点多),所以让我们将其分为两个角度:从实例/类外部(例如从普通函数调用,或从全局范围),和在实例/类内部(从对象的方法内部)。

从实例/类外部

从实例/类的外部来看,我们的规则非常简单且可预测。我们有两个操作符,如果处理实例或类static,每个操作符都会立即告诉我们:

->-object运算符-当我们访问实例时,总是使用这个运算符。$bob=新人;echo$bob->name;需要注意的是,调用Person->foo没有意义(因为Person是一个类,而不是实例)。因此,这是一个解析错误。::-scope解析运算符-始终用于访问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;
    }
}

使用对象运算符$foo->bar()对$foo(foo的一个实例)调用bar()方法将导致实例的$a版本。

这就是我们所期望的。

虽然::运算符的含义发生了变化。它取决于调用当前函数的上下文:

在静态上下文中在静态上下文中,使用::进行的任何调用也将是静态的。我们来看一个示例:Foo类{公共函数栏(){return Foo::baz();}公共函数baz(){return isset($this);}}调用Foo::bar()将静态调用baz()方法,因此不会填充$this。值得注意的是,在最新版本的PHP(5.3+)中,这将触发E_STRICT错误,因为我们静态调用非静态方法。在实例上下文中另一方面,在实例上下文中,使用::进行的调用取决于调用的接收方(我们正在调用的方法)。如果方法被定义为静态,那么它将使用静态调用。如果不是,它将转发实例信息。因此,查看上面的代码,调用$foo->bar()将返回true,因为“静态”调用发生在实例上下文中。

有道理?我不这么认为。这让人困惑。

快捷关键字

由于使用类名将所有内容联系在一起是相当肮脏的,PHP提供了3个基本的“快捷方式”关键字,以使范围解析更容易。

self-这是指当前类名。因此,self::baz()与Foo类(其上的任何方法)中的Foo::bax()相同。parent-指当前类的父级。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错误。因此,我强烈建议在实例方法中避免使用::运算符,除了这三个虚拟的“快捷”关键字(静态、自我和父关键字)。

注意事项#2

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

注意事项#3

通常情况下,您希望通过使用静态而不是自身来使用所谓的后期静态绑定。但请注意,它们不是同一回事,所以说“总是使用静态而不是自己”是非常短视的。相反,停下来想想你想做的调用,想想你是否希望子类能够覆盖静态解析的调用。

目标/目标

太糟糕了,回去读一读。它可能太长了,但它那么长,因为这是一个复杂的话题

TL/DR#2

好的,好的。简而言之,self用于引用类中的当前类名,其中$this引用当前对象实例。请注意,self是复制/粘贴快捷方式。您可以放心地用类名替换它,它会很好地工作。但$this是一个动态变量,无法提前确定(甚至可能不是您的类)。

TL/DR#3

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


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

换句话说,对静态成员或方法使用self,而对非静态成员和方法使用this。

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


此外,由于$this::尚未讨论。

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

http://ideone.com/7etRHy

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相反;


在PHP中,使用self关键字访问静态财产和方法。

问题是,无论method()是否声明为静态,都可以在任何地方用self::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。


这里有一个小的基准(第7.2.24节):

            Speed (in seconds)  Percentage
$this->     0.91760206222534    100
self::      1.0047659873962     109.49909865716
static::    0.98066782951355    106.87288857386

4000 000次运行的结果。结论:没关系。这是我使用的代码:

<?php

class Foo
{
  public function calling_this() { $this->called(); }
  public function calling_self() { self::called(); }
  public function calling_static() { static::called(); }
  public static function called() {}
}

$foo = new Foo();
$n = 4000000;
$times = [];

// warmup
for ($i = 0; $i < $n; $i++) { $foo->calling_this(); }
for ($i = 0; $i < $n; $i++) { $foo->calling_self(); }
for ($i = 0; $i < $n; $i++) { $foo->calling_static(); }

$start = microtime(true);
for ($i = 0; $i < $n; $i++) { $foo->calling_this(); }
$times["this"] = microtime(true)-$start;

$start = microtime(true);
for ($i = 0; $i < $n; $i++) { $foo->calling_self(); }
$times["self"] = microtime(true)-$start;

$start = microtime(true);
for ($i = 0; $i < $n; $i++) { $foo->calling_static(); }
$times["static"] = microtime(true)-$start;

$min = min($times);
echo $times["this"] . "\t" . ($times["this"] / $min)*100 . "\n";
echo $times["self"] . "\t" . ($times["self"] / $min)*100 . "\n";
echo $times["static"] . "\t" . ($times["static"] / $min)*100 . "\n";

根据php.net,这里有三个特殊的关键字:self、parent和static。它们用于从类定义内部访问财产或方法。

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


self是指当前类(在其中调用它),

$这是指当前对象。您可以使用static而不是self。

请参见示例:

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

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

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

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

输出:

 parent
 child

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

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

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

情况2:对于静态财产

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

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

$这需要类的实例self::不

每当您使用静态方法或静态属性并希望在没有实例化类对象的情况下调用它们时,都需要使用self:来调用它们,因为$this总是需要创建一个对象。


self::用于当前类的关键字,基本上用于访问静态成员、方法和常量。但是在$this的情况下,不能调用静态成员、方法和函数。

您可以在另一个类中使用self::关键字并访问静态成员、方法和常量。当它将从父类扩展时,在$this关键字的情况下也是如此。当另一个类将从父类扩展时,您可以访问该类中的非静态成员、方法和函数。

下面给出的代码是self::和$this关键字的示例。只需复制并粘贴代码文件中的代码,即可看到输出。

class cars{
    var $doors = 4;
    static $car_wheel = 4;

    public function car_features(){
        echo $this->doors . " Doors <br>";
        echo self::$car_wheel . " Wheels <br>";
    }
}

class spec extends cars{
    function car_spec(){
        print(self::$car_wheel . " Doors <br>");
        print($this->doors . " Wheels <br>");
    }
}

/********Parent class output*********/

$car = new cars;
print_r($car->car_features());

echo "------------------------<br>";

/********Extend class from another class output**********/


$car_spec_show = new spec;

print($car_spec_show->car_spec());