在PHP 5中,使用self
和之间有什么区别$this
?
什么时候合适?
在PHP 5中,使用self
和之间有什么区别$this
?
什么时候合适?
Answers:
使用
$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()
当前对象的确切类型的成员函数。如果对象是type X
,则调用X::foo()
。如果对象是type Y
,则调用Y::foo()
。但是X::foo()
总是使用self :: foo()来调用。
self
与范围解析运算符::
一起使用以引用当前类;这可以在静态和非静态上下文中完成。此外,使用$this
静态方法(而不是引用字段)是完全合法的。
$this::
呢
关键字自不不只是指“当前类的,至少不会在某种程度上,限制你的静态成员。在非静态成员的上下文中,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
)。
$this::
呢
$this::
;所有可能的情况都已被更常用的语法覆盖。根据你的意思,使用$this->
,self::
或static::
。
不要使用self::
,使用static::
自我的另一方面::值得一提。恼人的self::
是指定义时的范围而不是执行时的范围。考虑使用两种方法的简单类:
class Person
{
public static function status()
{
self::getStatus();
}
protected static function getStatus()
{
echo "Person is alive";
}
}
如果我们打电话,Person::status()
我们将看到“人还活着”。现在考虑当我们创建一个继承自此的类时会发生什么:
class Deceased extends Person
{
protected static function getStatus()
{
echo "Person is deceased";
}
}
进行调用时,Deceased::status()
我们希望看到“ Person已死”,但是我们看到的是“ Person仍然存在”,因为在定义调用时,作用域包含原始方法定义self::getStatus()
。
PHP 5.3有一个解决方案。该static::
解决运营商实施“后期静态绑定”,这是说,它必然要调用的类范围的一个奇特的方式。将行更改status()
为 static::getStatus()
,结果就是您所期望的。在较旧版本的PHP中,您将必须找到解决方法。
参见PHP文档
所以回答问题不是按要求...
$this->
引用当前对象(类的实例),而static::
引用类
getStatus
方法视为类实例而不是类的实例。
self::
,则可以减少容易混淆的,通过使用特定的类名,如MyClass::
。
为了真正理解在谈论self
vs 时我们在谈论什么$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
。
因此,我们说一个类是一个全局结构,一个对象是一个局部结构。不用担心这种有趣的->
语法,我们将对此进行一些介绍。
我们应该讨论的另一件事是,我们可以检查实例是否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(从对象的方法内部)。
从实例/类的外部,我们的规则非常简单且可预测。我们有两个运算符,每个运算符都会立即告诉我们是否要处理实例或静态类:
->
- 对象的操作 -当我们访问一个实例这是始终使用。
$bob = new Person;
echo $bob->name;
重要的是要注意,调用Person->foo
没有任何意义(因为它Person
是一个类,而不是实例)。因此,这是一个解析错误。
::
- 范围分辨率的操作员 -这总是被用来访问一类的静态属性或方法。
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
这就是我们的期望。
::
尽管运算符的含义有所变化。这取决于对当前函数的调用上下文:
在静态环境中
在静态上下文中,使用进行的任何调用::
也将是静态的。让我们看一个例子:
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::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
而不是使用所谓的Latex-Static-Binding self
。但是请注意,它们不是一回事,所以说“始终使用static
而不是代替self
确实是近视的。相反,停下来想一想您要进行的调用,并考虑是否希望子类能够覆盖该静态解析呼叫。
太糟糕了,请回头阅读。可能太长了,但是太长了,因为这是一个复杂的主题
好的。简而言之,self
用于引用一个类中的当前类名称,其中as $this
表示当前对象实例。请注意,这self
是复制/粘贴的快捷方式。您可以使用您的班级名称安全地替换它,它将正常运行。但是它$this
是一个动态变量,无法提前确定(甚至可能不是您的课程)。
如果使用对象运算符(->
),则您始终知道您正在处理实例。如果使用scope-resolution-operator(::
),则需要有关上下文的更多信息(我们是否已经在对象上下文中?我们是否在对象外部?等等)。
$this
如果您遵循“严格标准”并且不以静态方式调用未定义为静态的方法,则不会定义。我看到您在这里解释的结果:3v4l.org/WeHVM同意,真的很奇怪。
$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对对象一无所知。它只是与您传入的参数一起使用,就像不属于对象的普通函数一样。那么,如果我们不把它作为对象的一部分,那又何必呢?
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::$templates_path
在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()
将始终输出“ parent”,而$this->who()
将取决于对象具有的类。
现在我们可以看到self指的是调用它的类,而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
这是对非静态和静态成员变量正确使用$ 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();
?>
根据http://www.php.net/manual/zh/language.oop5.static.php,没有$self
。只有$this
,用于引用类(对象)的当前实例,而自身则可以用于引用类的静态成员。对象实例和类之间的区别在这里起作用。
我相信问题不在于您是否可以通过调用来调用类的静态成员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;
}
}
Fatal error: Access to undeclared static property: Person::$name in D:\LAMP\www\test.php on line 16
self
引用当前类(在其中被称为),
$this
引用当前对象。您可以使用静态而不是自我。参见示例:
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();
输出:亲子
$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
是因为无论原始类有什么扩展,都想要原始类。(非常,很少)
正如这里没有人谈论性能一样,这是我做的一个小型基准测试(5.6):
Name | Time | Percent
----------|---------|---------
$this-> | 0.99163 | 106.23%
self:: | 0.96912 | 103.82%
static:: | 0.93348 | 100%
这些是2000000次运行的结果,这是我使用的代码:
<?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();
1 / 2e9 s = 0.5 ns
use
关键字tbh,但是我没有PHP可以重做基准测试,而且我真的不希望重新安装它。
另外因为 $this::
尚未进行讨论。
仅出于提供信息的目的,从PHP 5.3开始,在处理实例化对象以获取当前作用域值时,与使用相比static::
,可以$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 :: 关键字,用于当前类,基本上用于访问静态成员,方法和常量。但是在$ 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());