self
在 PHP 5 中,使用和 和有什么区别$this
?
什么时候合适?
用于
$this
引用当前对象。用于self
引用当前类。换句话说,$this->member
用于非静态成员,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();
?>
下面是一个带有for 成员函数的多态性示例:$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();
?>
下面是一个使用for 成员函数来抑制多态行为的例子: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()
当前对象的确切类型的成员函数。如果对象是 oftype X
,它会调用X::foo()
. 如果对象是 oftype Y
,则调用Y::foo()
. 但是 self::foo()X::foo()
总是被调用。
来自http://www.phpbuilder.com/board/showthread.php?t=10354489:
关键字 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()
uses self::getTitle()
,所以 vtable 没有被使用,Person::getTitle()
而是被调用。在这两种情况下,我们都在处理实例化对象的方法,并且可以访问$this
被调用函数中的指针。
不要使用 self::
. 使用static::
*
self:: 还有一个方面值得一提。烦人的是,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 is dead”。但是,我们看到“Person is alive”,因为作用域包含定义调用时的原始方法定义self::getStatus()
。
PHP 5.3 有一个解决方案。解析运算符实现了“static::
后期静态绑定”,这是一种奇特的说法,它被绑定到所调用的类的范围内。将行更改status()
为static::getStatus()
,结果是您所期望的。在旧版本的 PHP 中,您必须找到一个组合来执行此操作。
请参阅PHP 文档
因此,要回答未按要求回答的问题...
$this->
指的是当前对象(类的一个实例),而static::
指的是一个类。
当我们谈论self
vs时,要真正理解我们在谈论什么$this
,我们需要在概念和实践层面上深入研究正在发生的事情。我真的不觉得任何答案都能恰当地做到这一点,所以这是我的尝试。
让我们从什么是类和对象开始。
那么,什么是类?很多人将其定义为对象的蓝图或模板。事实上,您可以在此处阅读更多关于 PHP 中的类。在某种程度上,这就是它的真实情况。我们来看一个类:
class Person {
public $name = 'my name';
public function sayHello() {
echo "Hello";
}
}
如您所知,该类上有一个名为的属性$name
和一个名为 的方法(函数)sayHello()
。
需要注意的是,该类是一个静态结构,这一点非常重要。这意味着 class ,一旦定义,在你看到的任何地方都是一样的。Person
另一方面,对象是所谓的类的实例。这意味着我们采用类的“蓝图”,并使用它来制作动态副本。该副本现在专门与它存储的变量相关联。因此,对实例的任何更改都是该实例的本地更改。
$bob = new Person;
$adam = new Person;
$bob->name = 'Bob';
echo $adam->name; // "my name"
我们使用操作符创建一个类的新实例new
。
因此,我们说一个类是一个全局结构,一个对象是一个局部结构。不用担心那个有趣的->
语法,我们稍后会详细介绍。
我们应该讨论的另一件事是,我们可以检查一个实例是否是instanceof
一个特定的类:如果实例是使用该类创建的,$bob instanceof Person
它返回一个布尔值,或者是.$bob
Person
Person
因此,让我们深入了解一个类实际包含的内容。一个类包含 5 种类型的“事物”:
属性- 将这些视为每个实例将包含的变量。
class Foo {
public $bar = 1;
}
静态属性- 将这些视为在类级别共享的变量。这意味着它们永远不会被每个实例复制。
class Foo {
public static $bar = 1;
}
方法- 这些是每个实例将包含的函数(并对实例进行操作)。
class Foo {
public function bar() {}
}
静态方法- 这些是在整个类中共享的函数。它们不对实例进行操作,而是仅对静态属性进行操作。
class Foo {
public static function bar() {}
}
常量- 类解析常量。这里不再深入,但为了完整性而添加:
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;
}
}
使用 object-operator:调用(的实例)bar()
上的方法将导致实例的版本为.$foo
Foo
$foo->bar()
$a
这就是我们所期望的。
运算符的含义::
虽然发生了变化。这取决于调用当前函数的上下文:
在静态上下文中
在静态上下文中,使用 using 进行的任何调用::
也将是静态的。让我们看一个例子:
class Foo {
public function bar() {
return Foo::baz();
}
public function baz() {
return isset($this);
}
}
调用Foo::bar()
将baz()
静态调用该方法,因此$this
不会被填充。值得注意的是,在最近的 PHP (5.3+) 版本中,这将触发E_STRICT
错误,因为我们正在静态调用非静态方法。
在实例上下文中
另一方面,在实例上下文中,使用 using::
进行的调用取决于调用的接收者(我们正在调用的方法)。如果方法定义为static
,那么它将使用静态调用。如果不是,它将转发实例信息。
因此,查看上面的代码,调用$foo->bar()
将返回true
,因为“静态”调用发生在实例上下文中。
有道理?没想到。这很令人困惑。
因为使用类名将所有内容联系在一起是相当肮脏的,所以 PHP 提供了 3 个基本的“快捷方式”关键字来使范围解析更容易。
self
- 这是指当前的类名。所以在类self::baz()
中是一样的(它上面的任何方法)。Foo::baz()
Foo
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()
在其中一种情况(子情况)中使用 来做到这一点。这就是使这种方法强大的原因。
请注意,调用上下文决定是否使用实例。所以:
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)
现在这里真的很奇怪。我们调用了一个不同的类,但是$this
传递给Foo::isFoo()
方法的是$bar
.
这可能会导致各种错误和概念上的 WTF-ery。所以我强烈建议避免::
在实例方法中使用运算符,除了这三个虚拟“快捷”关键字(static
、、self
和parent
)之外的任何东西。
请注意,每个人都共享静态方法和属性。这使得它们基本上是全局变量。与全局变量相同的问题。因此,除非您对真正的全局性感到满意,否则我会非常犹豫将信息存储在静态方法/属性中。
通常,您会希望通过使用static
而不是self
. 但请注意,它们不是一回事,所以说“总是使用static
而不是self
真的短视。相反,停下来想想你想要打的电话,想想你是否希望子类能够覆盖静态解析称呼。
太糟糕了,回去读一读。它可能太长了,但它就是那么长,因为这是一个复杂的话题
好的。简而言之,self
用于引用类中的当前类名,其中 as$this
指的是当前对象实例。请注意,这self
是一个复制/粘贴快捷方式。你可以安全地用你的类名替换它,它会正常工作。但是$this
是一个无法提前确定的动态变量(甚至可能不是您的班级)。
如果使用了对象运算符 ( ->
),那么您总是知道您正在处理一个实例。如果使用范围解析操作符 ( ::
),您需要有关上下文的更多信息(我们是否已经在对象上下文中?我们是否在对象之外?等等)。
self
(not $self) 指的是类的类型,而$this
指的是类的当前实例。 self
用于静态成员函数,以允许您访问静态成员变量。$this
在非静态成员函数中使用,并且是对调用成员函数的类的实例的引用。
因为this
是一个对象,所以你可以像这样使用它:$this->member
因为self
不是对象,所以基本上是自动引用当前类的类型。你像这样使用它:self::member
$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)可以在一类对象的所有实例之间共享。例如,所有数据库对象共享一个连接(静态 $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 是静态的(共享的),它将反映活动数据库对象的总数。您可能已经看到这种技术用于在数据库类的所有实例之间共享数据库连接。这样做是因为创建数据库连接需要很长时间,所以最好只创建一个并共享它(这称为单例模式)。
静态方法(即 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 对对象一无所知。它只是使用您传入的参数,就像不属于对象的普通函数一样。那么,如果我们不能将它作为对象的一部分,为什么还要麻烦呢?
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: Example: self::connection;
希望这有助于解决问题:)
从这篇博文:
self
指当前类self
可用于调用静态函数和引用静态成员变量self
可以在静态函数中使用self
也可以通过绕过 vtable 来关闭多态行为$this
指当前对象$this
可用于调用静态函数$this
不应该用来调用静态成员变量。改为使用self
。$this
不能在静态函数内部使用
在 PHP 中,您使用 self 关键字来访问静态属性和方法。
问题是您可以$this->method()
用self::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()
将始终输出“父级”,而$this->who()
将取决于对象具有的类。
现在我们可以看到 self 指的是调用它的类,而$this
指的是当前对象的类。
所以,你应该只在不可用时使用 self $this
,或者当你不想让后代类覆盖当前方法时。
在类定义中,$this
指的是当前对象,而self
指的是当前类。
有必要使用 来引用类元素self
,并使用 来引用对象元素$this
。
self::STAT // refer to a constant value
self::$stat // static variable
$this->stat // refer to an object variable
下面是对非静态和静态成员变量正确使用 $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();
?>
根据Static Keyword,没有任何$self
. 只有$this
, 用于引用类的当前实例(对象),而self
, 可用于引用类的静态成员。对象实例和类之间的区别在这里发挥作用。
$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
or的原因$this
。但是,有时您需要 self
,因为您想要原始类,而不管它扩展了什么。(非常,很少)
self
引用当前类(在其中调用它),
$this
指当前对象。您可以使用静态而不是自我。
请参阅示例:
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
我相信问题不在于你是否可以通过调用来调用类的静态成员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;
}
}
这是一个小基准(repl.it 上的7.2.24):
Speed (in seconds) Percentage
$this-> 0.91760206222534 100
self:: 1.0047659873962 109.49909865716
static:: 0.98066782951355 106.87288857386
4 000 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";
当self
与::
运算符一起使用时,它指的是当前类,这可以在静态和非静态上下文中完成。$this
指对象本身。此外,使用$this
调用静态方法(但不引用字段)是完全合法的。
我遇到了同样的问题,简单的答案是:
$this
需要类的实例self::
没有每当您使用静态方法或静态属性并且想要在没有实例化类的对象的情况下调用它们时,您需要使用self:
来调用它们,因为$this
总是需要创建一个对象。
$this
引用当前类对象,并self
引用当前类(非对象)。类是对象的蓝图。所以你定义了一个类,但是你构造了对象。
所以换句话说,使用self for static
and this for none-static members or methods
。
同样在子/父场景中,self / parent
主要用于标识子类和父类的成员和方法。
另外,由于$this::
尚未讨论。
仅供参考,从 PHP 5.3 开始,在处理实例化对象以获取当前范围值时,与 usingstatic::
不同,可以$this::
像这样使用。
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;
如果self
您想在不创建该类的对象/实例的情况下调用类的方法,从而节省RAM(有时为此目的使用 self),请使用此选项。换句话说,它实际上是在静态调用一个方法。用于this
对象透视。
案例1:使用self
可用于类常量
类A类{ 常量 FIXED_NUMBER = 4; 自我::POUNDS_TO_KILOGRAMS }
如果要在类之外调用它,请使用classA::POUNDS_TO_KILOGRAMS
访问常量
案例 2:对于静态属性
类 C { 公共函数 __construct() { 自我::$_counter++; $this->num = self::$_counter; } }
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());
根据 php.net,在此上下文中有三个特殊关键字self
:parent
和static
. 它们用于从类定义内部访问属性或方法。
$this
另一方面,用于调用任何类的实例和方法,只要该类是可访问的。