이름 해석 규칙
이름 해석 규칙
(PHP 5 >= 5.3.0, PHP 7, PHP 8)
본격적으로 규칙을 살펴보기 전에, 여기서 쓰게 될 중요한 이름 정의 몇 가지를 먼저 정리할게요.
네임스페이스 이름 정의
비정규화 이름(Unqualified name)
네임스페이스 구분자 없이 쓰는 식별자예요. 예를 들면 Foo 같은 이름이죠.
정규화 이름(Qualified name)
네임스페이스 구분자가 들어 있는 식별자예요. Foo\Bar 같은 형태죠.
완전 정규화 이름(Fully qualified name)
네임스페이스 구분자로 시작하는 식별자예요. \Foo\Bar처럼 이름 맨 앞에 구분자가 붙는 형태죠. 여기서 네임스페이스 \Foo도 완전 정규화 이름이에요.
상대 이름(Relative name)
namespace로 시작하는 식별자예요. namespace\Foo\Bar 같은 형태죠.
이제 이름들이 실제로 어떻게 해석되는지, 규칙을 순서대로 따라가 볼게요.
-
완전 정규화 이름은 항상 맨 앞의 네임스페이스 구분자를 뺀 이름으로 해석돼요. 예를 들어
\A\B는A\B로 해석되죠. -
상대 이름은 항상
namespace가 현재 네임스페이스로 바뀐 이름으로 해석돼요. 이름이 전역 네임스페이스에 있다면namespace\접두사는 그냥 빠져요. 예를 들어 네임스페이스X\Y안에서namespace\A는X\Y\A로 해석되고, 전역 네임스페이스에 있는 같은 이름은A로 해석돼요. -
정규화 이름에서는 이름의 첫 부분이 현재 클래스/네임스페이스 import 테이블에 따라 치환돼요. 예를 들어 네임스페이스
A\B\C가C로 import되어 있으면, 이름C\D\E는A\B\C\D\E로 바뀌죠. -
정규화 이름에서 적용되는 import 규칙이 없으면 현재 네임스페이스가 이름 앞에 붙어요. 예를 들어 네임스페이스
A\B안에서 이름C\D\E는A\B\C\D\E로 해석돼요. -
비정규화 이름은 해당 심볼 종류에 맞는 현재 import 테이블에 따라 치환돼요. 즉 클래스류 이름은 클래스/네임스페이스 import 테이블을, 함수 이름은 함수 import 테이블을, 상수는 상수 import 테이블을 따르죠. 예를 들어
use A\B\C;다음에new C()라고 쓰면A\B\C()라는 이름으로 해석돼요. 마찬가지로use function A\B\foo;다음에foo()라고 쓰면A\B\foo라는 이름으로 해석되죠. -
비정규화 이름에서 적용되는 import 규칙이 없고 그 이름이 클래스류 심볼을 가리키면 현재 네임스페이스가 이름 앞에 붙어요. 예를 들어 네임스페이스
A\B안에서new C()는A\B\C라는 이름으로 해석돼요. -
비정규화 이름에서 적용되는 import 규칙이 없고 그 이름이 함수나 상수를 가리키며, 코드가 전역 네임스페이스 밖에 있다면 이름은 런타임에 해석돼요. 코드가 네임스페이스
A\B안에 있다고 가정할 때, 함수foo()를 호출하면 이렇게 해석됩니다.- 현재 네임스페이스에서 함수를 찾아요.
A\B\foo()가 그 대상이죠. - 전역(global) 함수
foo()를 찾아 호출해 봐요.
- 현재 네임스페이스에서 함수를 찾아요.
예제 #1 이름 해석이 어떻게 일어나는지 살펴보기
<?php
namespace A;
use B\D, C\E as F;
// function calls
foo(); // first tries to call "foo" defined in namespace "A"
// then calls global function "foo"
\foo(); // calls function "foo" defined in global scope
my\foo(); // calls function "foo" defined in namespace "A\my"
F(); // first tries to call "F" defined in namespace "A"
// then calls global function "F"
// class references
new B(); // creates object of class "B" defined in namespace "A"
// if not found, it tries to autoload class "A\B"
new D(); // using import rules, creates object of class "D" defined in namespace "B"
// if not found, it tries to autoload class "B\D"
new F(); // using import rules, creates object of class "E" defined in namespace "C"
// if not found, it tries to autoload class "C\E"
new \B(); // creates object of class "B" defined in global scope
// if not found, it tries to autoload class "B"
new \D(); // creates object of class "D" defined in global scope
// if not found, it tries to autoload class "D"
new \F(); // creates object of class "F" defined in global scope
// if not found, it tries to autoload class "F"
// static methods/namespace functions from another namespace
B\foo(); // calls function "foo" from namespace "A\B"
B::foo(); // calls method "foo" of class "B" defined in namespace "A"
// if class "A\B" not found, it tries to autoload class "A\B"
D::foo(); // using import rules, calls method "foo" of class "D" defined in namespace "B"
// if class "B\D" not found, it tries to autoload class "B\D"
\B\foo(); // calls function "foo" from namespace "B"
\B::foo(); // calls method "foo" of class "B" from global scope
// if class "B" not found, it tries to autoload class "B"
// static methods/namespace functions of current namespace
A\B::foo(); // calls method "foo" of class "B" from namespace "A\A"
// if class "A\A\B" not found, it tries to autoload class "A\A\B"
\A\B::foo(); // calls method "foo" of class "B" from namespace "A"
// if class "A\B" not found, it tries to autoload class "A\B"