php 에서의 static 변수

정적 멤버(static member) 

PHP5에서 정적 멤버를 static 키워드를 이용하여 정의하여 사용할 수 있습니다. 

visibility static $변수이름 = 변수값; 
visibility static function 함수이름(인수리스트) { 함수내용 }; 
visibility 위치에는 정적 멤버의 가시범위(visibility)를 제한할 수 있는 PPP 접근제한자(private/protected/public access modifier)를 지정할 수 있습니다. 생략하면 PHP4와의 호환성을 고려하여 멤버를 public로 처리합니다. 

정적 멤버는 객체를 생성하지 않고도 접근할 수 있도록 해줍니다. 

[ 예제출처:php.net ] 
<?php  
class Foo  
{  
  public static $my_static = 'foo';  
  
  public function staticValue() {  
      return self::$my_static;  
  }  
}  
  
class Bar extends Foo  
{  
  public function fooStatic() {  
      return parent::$my_static;  
  }  
}  
  
print Foo::$my_static . "\n";  
  
$foo = new Foo();  
print $foo->staticValue() . "\n";  
  
print Bar::$my_static . "\n";  
$bar = new Bar();  
print $bar->fooStatic() . "\n";  
?>  
정적 멤버 변수(static member variable) 

<?php  
class test_class {  
  public static $static_var = "my static variable's value\n";  
  
  public function get_static() {  
      return self::$static_var;  
  }  
  
  public function set_static($val) {  
      self::$static_var = $val . "\n";  
  }  
}  
  
print test_class::$static_var;  
  
$obj = new test_class();  
$obj->set_static("my static variable's new value");  
  
$obj2 = new test_class();  
print $obj2->get_static();  
?>  
위에서 볼 수 있듯이 정적 멤버 변수로 선언된 변수는 어느 인스턴스에서 수정되어도 모든 인스턴스에 적용이 됩니다. 위에서 17번째 줄에 보이듯이 $obj 인스턴스에 의해 수정된 정적 멤버 변수는 $obj2의 다른 인스턴스에도 모두 영향을 미칩니다. 

출력 결과는 아래와 같습니다. 

my static variable's value 
my static variable's new value 
클래스 상수와 마찬가지로 정적 멤버 변수는 객체를 통해서 접근할 수 없습니다. 이것은 정적 멤버 함수가 객체를 통해서 접근할 수 있는 것과 다르다는 점에 주의해야 합니다. 따라서 클래스 내부에서만 사용되는 $this 키워드를 이용해서도 접근이 불가능하며 반드시 범위지정연산자(::) 앞에 클래스명, self 또는 parent 키워드를 이용해서 접근해야만 합니다. 

<?php  
class test_class {  
  public static $static_var = "my static variable's value\n";  
}  
  
$obj = new test_class();  
  
print $obj->static_var;  
// Notice: Undefined property: test_class::$static_var in xxx.php on line 8  
  
// $obj::static_var is not possible  
// Parse error: parse error, unexpected T_PAAMAYIM_NEKUDOTAYIM  
//  in xxx.php on line nnn  
?>  
정적 멤버 함수(static method) 

정적 멤버 함수는 객체를 생성하지 않고도 접근할 수 있도록 해줍니다. 

<?php  
class my_class {  
  public static function hello_world() {  
    print "Hello, world";  
  }  
}  
  
my_class::hello_world();  
?>  
생성된 객체를 통해서 멤버에 접근할 수 있습니다. 

<?php  
class my_class {  
  public static function hello_world() {  
    print "Hello, world";  
  }  
}  
  
$obj = new my_class;  
$obj->hello_world();  
?>  
그러나 객체 생성없이 접근하는 경우도 있기 때문에 의사 변수(pseudo variable) $this를 정적 멤버 함수 내에서 사용해서는 안됩니다. 

<?php  
class my_class {  
  public static function hello_world() {  
    print "Hello, world";  
  }  
  
  public static function hello() {  
    $this->hello_world();  
  }  
}  
  
$obj = new my_class;  
$obj->hello();  
// Fatal error: Using $this when not in object context in xxx.php on line 8  
?>  
정적멤버가 아닌 메쏘드를 정적으로 호출하는 것은 E_STRICT 레벨의 경고를 발생시킵니다. 

<?php  
class my_class {  
  public function hello_world() {  
      print "Hello, world";  
  }  
}  
  
error_reporting(E_ALL | (defined('E_STRICT')? E_STRICT : 0));  
  
my_class::hello_world();  
// Strict Standards: Non-static method my_class::hello_public()  
//  should not be called statically in xxx.php on line 10  
?>  
싱글턴 패턴(singleton pattern) 

정적 멤버 변수를 가장 유용하게 활용하는 곳이 있다면 디자인 패턴 중 싱글턴 패턴(singleton pattern)일 것입니다. 

싱글턴 패턴은 프로그램이 동작할 때 클래스의 인스턴스가 반드시 하나만 존재하도록 해주는 패턴으로 매우 중요한 요소입니다만 PHP4에서는 정적멤버, PPP 접근제한자, 인터페이스 등의 객체지향언어의 핵심기능을 빠진 상태에서 구현이 사실상 불가능하였습니다. 

PHP5부터 제공되기 시작한 정적멤버 및 PPP 접근제한자 등의 특성을 이용하여 디자인 패턴 중에서도 가장 널리 사용되는 싱글턴 패턴을 구현해 보겠습니다. 소스는 php.net의 document에서 발췌하여 수정한 것입니다. 

<?php  
class Example {  
  // Hold an instance of the class  
  private static $instance;  
      
  // Prevents direct creation of object  
  private function __construct() {  
      echo 'I am constructed';  
  }  
  
  // The singleton method  
  public static function singleton() {  
      if (!isset(self::$instance)) {  
        $c = __CLASS__;  
        self::$instance = new $c;  
      }  
      return self::$instance;  
  }  
      
  // Example method  
  public function bark() {  
      echo 'Woof!';  
  }  
  
  // Prevent users to clone the instance  
  private function __clone() { }  
}  
  
//This allows a single instance of the Example class to be retrieved.  
  
// This will always retrieve a single instance of the class  
$test = Example::singleton();  
$test->bark();  
?>  
클래스 밖에서 singleton() 메쏘드를 이용하지 않고 아래 코드와 같이 new 또는 clone 연산자를 이용하여 객체를 생성하려면 Fatal 에러가 발생하기 때문에 프로그램상에서 인스턴스는 단 1개만 존재할 수 있습니다. 

<?php  
class Example {  
  // Hold an instance of the class  
  private static $instance;  
      
  // Prevents direct creation of object  
  private function __construct() {  
      echo 'I am constructed';  
  }  
  
  // The singleton method  
  public static function singleton() {  
      if (!isset(self::$instance)) {  
        $c = __CLASS__;  
        self::$instance = new $c;  
      }  
      return self::$instance;  
  }  
      
  // Example method  
  public function bark() {  
      echo 'Woof!';  
  }  
  
  // Prevent users to clone the instance  
  private function __clone() { }  
}  
  
// This would fail because the constructor is private  
$test = new Example;  
// Fatal error: Call to private Example::__construct()  
//  from invalid context in xxx.php on line 30  
  
// This will issue an E_USER_ERROR.  
$test_clone = clone $test;  
// Fatal error: Call to private Example::__clone() from context ''  
//  in xxx.php on line 35  
?>  
다중쓰레드(multi thread)를 지원하지 않는 PHP에서야 별 상관없는 이야기이지만 자바와 같이 다중쓰레드를 지원하는 프로그램에서는 위의 코드도 완벽하지 않습니다. 여러 개의 쓰레드가 거의 동시에 singleton() 메쏘드를 호출한다고 가정한다면 순간적으로 여러 개의 인스턴스가 생성될 수 있습니다. 따라서 singleton() 메쏘드는 단 하나의 쓰레드만 접근할 수 있도록 제한할 필요가 있습니다. 이를 위한 연산자가 synchronized입니다. 다음은 "Java 언어로 배우는 디자인 패턴 입문 - (주)영진닷컴" p.445에 나오는 synchronized 연산자를 적용한 싱글턴 페턴 클래스 예제입니다. 

public class Singleton {  
  private static Singleton singleton = null;  
  private Singleton() {  
      System.out.println("인스턴스를 생성했습니다");  
      slowdown();  
  }  
  public static synchronized Singleton getInstance() {  
      if (singleton == null) {  
        singleton = new Singleton();  
      }  
      return singleton;  
  }  
  private void slowdown() {  
      try {  
        Thread.sleep(1000);  
      } catch (InterruptedException e) {  
      }  
  }  
}  
위 소스에서 slowdown() 메쏘드는 다중쓰레드에 의해 여러 개의 인스턴스가 생성되는지 확인하기 위해 프로그램의 동작 속도를 일부로 느리게 하기 위해 첨가된 코드입니다.

댓글

이 블로그의 인기 게시물

PYTHONPATH, Python 모듈 환경설정

You can use Sublime Text from the command line by utilizing the subl command

git 명령어

[gRPC] server of Java and client of Typescript

[Ubuntu] Apache2.4.x 설치

Create topic on Kafka with partition count, 카프카 토픽 생성하기

리눅스의 부팅과정 (프로세스, 서비스 관리)

Auto-populate a calendar in an MUI (Material-UI) TextField component

The pierce selector in Puppeteer