PHP5 객체모델 > Static 멤버
PHP 2009. 9. 9. 11:25
정적 멤버(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() 메쏘드는 다중쓰레드에 의해 여러 개의 인스턴스가 생성되는지 확인하기 위해 프로그램의 동작 속도를 일부로 느리게 하기 위해 첨가된 코드입니다.
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() 메쏘드는 다중쓰레드에 의해 여러 개의 인스턴스가 생성되는지 확인하기 위해 프로그램의 동작 속도를 일부로 느리게 하기 위해 첨가된 코드입니다.