# Static V.S. Self in PHP

# Date: 2019/April/29


# 60 seconds

# Difference

# Static

PHP Manual的定義 (opens new window)

Declaring class properties or methods as static makes them accessible without needing an instantiation of the class. A property declared as static cannot be accessed with an instantiated class object (though a static method can).

  • Static properties cannot be accessed through the object using the arrow operator ->.
  • 物件不需要實體化便能使用;
  • 用來定義靜態變數 (也能拿來做Singleton單例模式)
  • 用來做延遲靜態綁定(Late Static Bindings)
  • 當存取物件方法/屬性/變數時, static::能存取呼叫時的物件所屬的方法/屬性/變數
  • 指向調用者本身

# Self

  • 相對於static, self存取物件屬性時則是使用當下物件的屬性
  • 指向當前定義之處

以下面這個簡單的例子來說, test()裡面第一個 self::sayMyName() 會呼叫Ancestor::sayMyName();
接著 static::sayMyName() 才會去執行自己的Offspring::sayMyName().

class Ancestor
{
    public static function sayMyName()
    {
        return "I am Ancestor";
    }

    public static function test()
    {
        echo self::sayMyName();
        echo "\n";
        echo static::sayMyName();
    }
}

class Offspring extends Ancestor
{
    public static function sayMyName()
    {
        return "I am Offspring";
    }
}

Offspring::test();
// I am Ancestor
// I am Offspring
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

另外一個例子:

這個例子可以看到self::class是從定義的地方(Ancestor)取得的;
static::class則是通過延遲靜態綁定, 也就是實際呼叫的Offspring的類別名稱.

class Ancestor
{
    public static function sayMyName()
    {
        return self::class;
    }

    public static function sayMyNameStatically()
    {
        return static::class;
    }
}

class Offspring extends Ancestor
{
    //
}

var_dump(Offspring::sayMyName()); // string(8) "Ancestor"
var_dump(Offspring::sayMyNameStatically());string(8) // string(9) "Offspring"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

PHP官方文件的解說:

TIP

Static references to the current class like self:: or _CLASS_ are resolved using the class in which the function belongs, as in where it was defined






 











class A {
    public static function who() {
        echo __CLASS__;
    }
    public static function test() {
        self::who();
    }
}

class B extends A {
    public static function who() {
        echo __CLASS__;
    }
}

B::test(); // Output is A
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

在官方文件裡可以看到類似的程式碼, 使用static做靜態綁定;

TIP

Late static bindings tries to solve that limitation by introducing a keyword that references the class that was initially called at runtime. Basically, a keyword that would allow you to reference B from test() in the previous example. It was decided not to introduce a new keyword but rather use static that was already reserved.






 












class A {
    public static function who() {
        echo __CLASS__;
    }
    public static function test() {
        static::who(); // Here comes Late Static Bindings
    }
}

class B extends A {
    public static function who() {
        echo __CLASS__;
    }
}

B::test(); // Output is B
?>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 60 minutes

class Parents
{
    public static function testWithSelf()
    {
        return self::hello();
    }

    public static function testWithStatic()
    {
        return static::hello();
    }

    public static function hello()
    {
        return "It is parent";
    }
}

class Child extends Parents
{
    public static function hello()
    {
        return "It is child";
    }
}

# Parent
var_dump(Parents::testWithSelf()); // string(11) "It is parent"
var_dump(Parents::testWithStatic()); // string(11) "It is parent"
# Child
var_dump(Child::testWithSelf()); // string(11) "It is parent"
var_dump(Child::testWithStatic()); // string(10) "It is child"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

這個例子中, ParentsChild各自實踐了一個方法hello(), 回傳不同的值;
再利用static call與self call做呼叫.

Child這個繼承了Parents的子類別要呼叫test()時,
可以看到static呼叫的是子類別自身的hello(), self則是呼叫了父類別的hello().


另一個官方的範例, 與屬性的存取有關係.

TIP

In non-static contexts, the called class will be the class of the object instance. Since $this-> will try to call private methods from the same scope, using static:: may give different results. Another difference is that static:: can only refer to static properties.







 

























class A {
    private function foo() {
        echo "success!\n";
    }
    public function test() {
        $this->foo();
        static::foo();
    }
}

class B extends A {
   /* foo() will be copied to B, hence its scope will still be A and
    * the call be successful */
}

class C extends A {
    private function foo() {
        /* original method is replaced; the scope of the new one is C */
    }
}

$b = new B();
$b->test();
$c = new C();
$c->test();

// success!
// success!
// success!
//
// Fatal error: Uncaught Error: Call to private method C::foo() from context 'A'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

test()沒辦法存取Class C裡的foo(), 因此回報錯誤.


Late Static Bindings

A "forwarding call" is a static one that is introduced by self::, parent::, static::, or, if going up in the class hierarchy, forward_static_call().

This feature was named "late static bindings" with an internal perspective in mind. "Late binding" comes from the fact that static:: will not be resolved using the class where the method is defined but it will rather be computed using runtime information. It was also called a "static binding" as it can be used for (but is not limited to) static method calls.

這個範例則是指出forwarding call與non-forwarding call的差別:

TIP

Late static bindings' resolution will stop at a fully resolved static call with no fallback. On the other hand, static calls using keywords like parent:: or self:: will forward the calling information.













 
 
 






















class A {
    public static function foo() {
        static::who();
    }

    public static function who() {
        echo __CLASS__."\n";
    }
}

class B extends A {
    public static function test() {
        A::foo();
        parent::foo();
        self::foo();
    }

    public static function who() {
        echo __CLASS__."\n";
    }
}
class C extends B {
    public static function who() {
        echo "Hola\n"; // Add output to make it more clear
        echo __CLASS__."\n";
    }
}

C::test();

// Output:
// A
// Hola
// C
// Hola
// C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

A::foo() 明確的指出了要呼叫者與呼叫的方法, 所以輸出是A (non-forwarding call);
可是當執行到14與15行時, 使用了forwarding call的關鍵字;
所以真正的呼叫者是Class C, 並執行 C::test(),


# Static variables

TIP

Another important feature of variable scoping is the static variable. A static variable exists only in a local function scope, but it does not lose its value when program execution leaves this scope.

由下面這個範例可以看到靜態變數的特性:

class Calculator
{
    public static $count = 0;
    public $sum = 0;

    public function __construct()
    {
        self::$count++;
        $this->sum++;
    }

    public function countToTen()
    {
        self::$count = 10;
        $this->sum = 10;
    }
}

$a = new Calculator;
$b = new Calculator;
$c = new Calculator;

echo Calculator::$count . "\n"; // 3
echo $a::$count . "\n"; // 3
echo $b::$count . "\n"; // 3
echo $c::$count . "\n"; // 3
echo $a->sum . "\n"; // 1

$a->countToTen();
echo $a::$count . "\n"; // 10
echo $a->sum . "\n"; // 10

echo $b::$count . "\n"; // 10
echo $b->sum . "\n"; // 1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

# Singleton

這邊直接參考他人寫好的範例(中文 (opens new window) / English (opens new window))

final class Singleton
{
    /**
     * @var Singleton
     */
    private static $instance;

    /**
     * gets the instance via lazy initialization (created on first usage)
     */
    public static function getInstance(): Singleton
    {
        if (null === static::$instance) {
            static::$instance = new static();
        }
        return static::$instance;
    }

    /**
     * is not allowed to call from outside to prevent from
     * creating multiple instances, to use the singleton,
     * you have to obtain the instance from Singleton::getInstance() instead
     */
    private function __construct()
    {
    }

    /**
     * prevent the instance from being cloned
     * (which would create a second instance of it)
     */
    private function __clone()
    {
    }

    /**
     * prevent from being unserialized
     * (which would create a second instance of it)
     */
    private function __wakeup()
    {
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

可以看到單例模式把所有有可能複製這個類別的方法都封鎖起來, 確保同一時間只能得到一個instance.

WARNING

單例模式被公認為反面模式(Anti-pattern), 需特別注意

另外還有幾篇有關Laravel中資料庫連線與單例模式的問題, 可以看看下面的其他參考資料.


# References


# 其他參考資料

PHP Manual (2019). Variable scope (opens new window).

PHP Manual (2019). Late Static Bindings (opens new window).

programmerinterview (2019). In PHP, what is the difference between self and static? (opens new window).

learnku (2018). PHP 设计模式全集 2018 - 单例模式(Singleton) (opens new window).

Liebler, D (2016). domnikl/DesignPatternsPHP - Creational/Singleton (opens new window). Github.

lukasgeiter (2014, Dec). Is the database class in Laravel use the “Singleton Pattern”? (opens new window). StackOverflow.

Raphaelson, J (2008, Oct). Global or Singleton for database connection? (opens new window). StackOverflow.

leoyang (2018). Laravel Database——数据库服务的启动与连接 (opens new window). learnku

leoyang (2018). Laravel Database——数据库服务的启动与连接 (on GitBook) (opens new window). GitBook

Last Updated: 2022/7/7 上午8:34:36