네임스페이스

네임스페이스 (Namespaces)

PHP 언어 레퍼런스 — 네임스페이스 챕터 번역입니다. (PHP 5 >= 5.3.0, PHP 7, PHP 8)

네임스페이스를 한마디로 말하면 항목들을 감싸서 묶는 방법이에요. 운영체제의 디렉터리가 관련 파일들을 한곳에 모아 두는 것과 같은 원리죠. 예를 들어 foo.txt 파일은 /home/greg 디렉터리에도, /home/other 디렉터리에도 존재할 수 있어요. 하지만 같은 디렉터리 안에 foo.txt 두 개가 함께 있을 수는 없죠. 그리고 /home/greg 바깥에서 그 파일에 접근하려면 파일 이름 앞에 디렉터리 이름을 붙여서 /home/greg/foo.txt처럼 써야 해요. 이 원리가 그대로 프로그래밍 세계의 네임스페이스로 이어져요.

PHP에서 네임스페이스는 라이브러리나 애플리케이션을 만들 때 재사용 가능한 코드 요소를 작성하다 보면 꼭 만나게 되는 두 가지 문제를 풀기 위해 설계됐어요.

  • 내가 만든 코드와 PHP 내부 클래스/함수/상수, 또는 서드파티 클래스/함수/상수 사이에서 벌어지는 이름 충돌
  • 위 문제를 완화하려고 만든 Extra_Long_Names 같은 긴 이름을 별칭(alias)으로 줄여서 코드 가독성을 높이는 것

PHP 네임스페이스는 관련된 클래스, 인터페이스, 함수, 상수를 묶는 방법을 제공해요. PHP에서 네임스페이스 문법이 어떻게 생겼는지 한번 볼게요.

<?php
namespace my\name; // see "Defining Namespaces" section

class MyClass {}
function myfunction() {}
const MYCONST = 1;

$a = new MyClass;
print $a::class . "\n";
$c = new \my\name\MyClass; // see "Global Space" section
print $c::class . "\n";

$a = strlen('hi'); // see "Using namespaces: fallback to global
                   // function/constant" section
print $a . "\n";

$d = namespace\MYCONST; // see "namespace operator and __NAMESPACE__
                        // constant" section
print $d . "\n";
$d = __NAMESPACE__ . '\MYCONST';
echo constant($d); // see "Namespaces and dynamic language features" section

네임스페이스 이름은 대소문자를 구분하지 않아요.

PHP라는 네임스페이스 이름과, PHP\Classes처럼 그 이름으로 시작하는 복합 이름은 내부 언어용으로 예약되어 있어서 사용자 코드에서는 쓰면 안 돼요.

네임스페이스 정의하기 (Defining namespaces)

네임스페이스 안에는 유효한 PHP 코드라면 무엇이든 넣을 수 있지만, 실제로 네임스페이스의 영향을 받는 코드는 클래스(추상 클래스, 트레이트, enum 포함), 인터페이스, 함수, 상수뿐이에요.

네임스페이스는 namespace 키워드로 선언해요. 네임스페이스를 포함하는 파일은 다른 모든 코드보다 위, 파일 맨 위에서 네임스페이스를 선언해야 해요. 단 하나의 예외가 있는데, 바로 declare 키워드예요.

예제 1 — 단일 네임스페이스 선언

<?php
namespace MyProject;

const CONNECT_OK = 1;
class Connection { /* ... */ }
function connect() { /* ... */ }

네임스페이스 선언에는 완전히 정규화된 이름(즉 백슬래시로 시작하는 이름)을 쓸 수 없어요. 그런 이름은 상대 네임스페이스 표현식으로 해석되기 때문이에요.

네임스페이스 선언 앞에 올 수 있는 코드 구성은 소스 파일의 인코딩을 정하는 declare 문이 유일해요. 게다가 네임스페이스 선언 앞에는 공백조차도 포함해 어떤 비-PHP 코드도 오면 안 돼요.

예제 2 — 단일 네임스페이스 선언

<html>
<?php
namespace MyProject; // fatal error - namespace must be the first statement in the script

또한 다른 PHP 구성과 달리 같은 네임스페이스를 여러 파일에 정의할 수 있어요. 그래서 한 네임스페이스의 내용을 파일시스템의 여러 파일에 걸쳐 나눠 담을 수 있죠.

하위 네임스페이스 (Sub-namespaces)

디렉터리와 파일처럼 PHP 네임스페이스도 계층 구조를 지정할 수 있어요. 그래서 네임스페이스 이름을 단계를 두고 정의할 수 있죠.

예제 1 — 계층을 가진 단일 네임스페이스 선언

<?php
namespace MyProject\Sub\Level;

const CONNECT_OK = 1;
class Connection { /* ... */ }
function connect() { /* ... */  }

위 예제는 상수 MyProject\Sub\Level\CONNECT_OK, 클래스 MyProject\Sub\Level\Connection, 함수 MyProject\Sub\Level\connect를 만들어요.

한 파일에 여러 네임스페이스 정의하기 (Defining multiple namespaces)

한 파일 안에 여러 네임스페이스를 선언할 수도 있어요. 허용되는 문법은 두 가지예요.

예제 1 — 여러 네임스페이스 선언, 단순 조합 문법

<?php
namespace MyProject;

const CONNECT_OK = 1;
class Connection { /* ... */ }
function connect() { /* ... */  }

namespace AnotherProject;

const CONNECT_OK = 1;
class Connection { /* ... */ }
function connect() { /* ... */  }

이 문법은 여러 네임스페이스를 한 파일에 합칠 때 권장되지 않아요. 대신 아래의 중괄호 문법을 쓰는 걸 권장해요.

예제 2 — 여러 네임스페이스 선언, 중괄호 문법

<?php
namespace MyProject {

const CONNECT_OK = 1;
class Connection { /* ... */ }
function connect() { /* ... */  }
}

namespace AnotherProject {

const CONNECT_OK = 1;
class Connection { /* ... */ }
function connect() { /* ... */  }
}

여러 네임스페이스를 같은 파일에 합치는 것은 코딩 관행상 강력히 권장되지 않아요. 주로 쓰이는 경우는 여러 PHP 스크립트를 한 파일로 합칠 때예요.

네임스페이스 없는 전역 코드와 네임스페이스 있는 코드를 합칠 때는 중괄호 문법만 지원돼요. 전역 코드는 아래처럼 네임스페이스 이름이 없는 namespace 문으로 감싸면 돼요.

예제 3 — 여러 네임스페이스와 네임스페이스 없는 코드 선언

<?php
namespace MyProject {

const CONNECT_OK = 1;
class Connection {
    public static function start() {
        print __METHOD__ . "\n";
    }
}

function connect() {
    print __FUNCTION__ . "\n";
}
}

namespace { // global code
print strlen("hi") . "\n";
MyProject\connect();
MyProject\Connection::start();
}

네임스페이스 중괄호 바깥에는 시작 declare 문을 빼고는 어떤 PHP 코드도 존재할 수 없어요.

예제 4 — 여러 네임스페이스와 네임스페이스 없는 코드 선언

<?php
declare(strict_types=1);
namespace MyProject {

const CONNECT_OK = 1;
class Connection {
    public static function start() {
        print __METHOD__ . "\n";
    }
}

function connect() {
    print __FUNCTION__ . "\n";
}
}

namespace { // global code
print strlen("hi") . "\n";
MyProject\connect();
MyProject\Connection::start();
}

네임스페이스 사용: 기초 (Basics)

네임스페이스 사용법을 논하기 전에, PHP가 코드가 요청하는 네임스페이스 요소를 어떻게 알아내는지 이해하는 게 중요해요. PHP 네임스페이스를 파일시스템에 비유하면 아주 간단해요. 파일시스템에서 파일에 접근하는 방법은 세 가지가 있어요.

  • foo.txt 같은 상대 파일 이름. 이것은 currentdirectory/foo.txt로 풀리는데, 여기서 currentdirectory는 현재 위치한 디렉터리예요. 그래서 현재 디렉터리가 /home/foo라면 이 이름은 /home/foo/foo.txt로 풀려요.
  • subdirectory/foo.txt 같은 상대 경로 이름. 이것은 currentdirectory/subdirectory/foo.txt로 풀려요.
  • /main/foo.txt 같은 절대 경로 이름. 이것은 그대로 /main/foo.txt로 풀려요.

같은 원리가 PHP의 네임스페이스 요소에도 적용돼요. 예를 들어 클래스 이름은 세 가지 방식으로 참조할 수 있어요.

  • 비정규화 이름(unqualified name), 즉 접두사가 없는 클래스 이름(예: $a = new foo(); 또는 foo::staticmethod();). 현재 네임스페이스가 currentnamespace이면 이것은 currentnamespace\foo로 풀려요. 코드가 전역(네임스페이스 없는) 코드라면 foo로 풀리죠.
    • 주의할 점 하나: 함수와 상수의 비정규화 이름은, 해당 네임스페이스의 함수나 상수가 정의돼 있지 않으면 전역 함수·상수로 폴백돼요. 자세한 내용은 "전역 공간으로의 폴백"을 봐요.
  • 정규화 이름(qualified name), 즉 접두사가 붙은 클래스 이름(예: $a = new subnamespace\foo(); 또는 subnamespace\foo::staticmethod();). 현재 네임스페이스가 currentnamespace이면 이것은 currentnamespace\subnamespace\foo로 풀려요. 전역 코드라면 subnamespace\foo로 풀리죠.
  • 완전히 정규화된 이름(fully qualified name), 즉 전역 접두사 연산자가 붙은 이름(예: $a = new \currentnamespace\foo(); 또는 \currentnamespace\foo::staticmethod();). 이것은 항상 코드에 적힌 그대로의 이름 currentnamespace\foo로 풀려요.

실제 코드에서 이 세 가지 문법이 어떻게 쓰이는지 볼게요.

file1.php

<?php
namespace Foo\Bar\subnamespace;

const FOO = 1;
function foo() {}
class foo
{
    static function staticmethod() {}
}

file2.php

<?php
namespace Foo\Bar;
include 'file1.php';

const FOO = 2;
function foo() {}
class foo
{
    static function staticmethod() {}
}

/* Unqualified name */
foo(); // resolves to function Foo\Bar\foo
foo::staticmethod(); // resolves to class Foo\Bar\foo, method staticmethod
echo FOO; // resolves to constant Foo\Bar\FOO

/* Qualified name */
subnamespace\foo(); // resolves to function Foo\Bar\subnamespace\foo
subnamespace\foo::staticmethod(); // resolves to class Foo\Bar\subnamespace\foo,
                                  // method staticmethod
echo subnamespace\FOO; // resolves to constant Foo\Bar\subnamespace\FOO

/* Fully qualified name */
\Foo\Bar\foo(); // resolves to function Foo\Bar\foo
\Foo\Bar\foo::staticmethod(); // resolves to class Foo\Bar\foo, method staticmethod
echo \Foo\Bar\FOO; // resolves to constant Foo\Bar\FOO

어떤 전역 클래스·함수·상수든 접근하려면 \strlen()이나 \Exception, \INI_ALL처럼 완전히 정규화된 이름을 쓸 수 있다는 점을 기억해 두면 좋아요.

예제 1 — 네임스페이스 안에서 전역 클래스·함수·상수에 접근하기

<?php
namespace Foo;

function strlen() {}
const INI_ALL = 3;
class Exception {}

print \strlen('hi') . "\n"; // calls global function strlen
print \INI_ALL . "\n"; // accesses global constant INI_ALL
$c = new \Exception('error'); // instantiates global class Exception
print $c::class . "\n";

네임스페이스와 동적 언어 기능 (Dynamic language features)

PHP의 네임스페이스 구현은 언어 자체의 동적 특성의 영향을 받아요. 아래 예제 같은 코드를 네임스페이스 코드로 바꾸려면:

예제 1 — 요소에 동적으로 접근하기 (example1.php)

<?php
class classname
{
    function __construct()
    {
        echo __METHOD__,"\n";
    }
}
function funcname()
{
    echo __FUNCTION__,"\n";
}
const constname = "global";

$a = 'classname';
$obj = new $a; // prints classname::__construct
$b = 'funcname';
$b(); // prints funcname
echo constant('constname'), "\n"; // prints global

완전히 정규화된 이름(네임스페이스 접두사가 붙은 클래스 이름)을 사용해야 해요. 동적 클래스 이름·함수 이름·상수 이름 안에서는 정규화 이름과 완전히 정규화된 이름 사이에 차이가 없기 때문에 맨 앞 백슬래시는 필요 없어요.

예제 2 — 네임스페이스 요소에 동적으로 접근하기

<?php
namespace namespacename;
class classname
{
    function __construct()
    {
        echo __METHOD__,"\n";
    }
}
function funcname()
{
    echo __FUNCTION__,"\n";
}
const constname = "namespaced";

/* note that if using double quotes, "\\namespacename\\classname" must be used */
$a = '\namespacename\classname';
$obj = new $a; // prints namespacename\classname::__construct
$a = 'namespacename\classname';
$obj = new $a; // also prints namespacename\classname::__construct
$b = 'namespacename\funcname';
$b(); // prints namespacename\funcname
$b = '\namespacename\funcname';
$b(); // also prints namespacename\funcname
echo constant('\namespacename\constname'), "\n"; // prints namespaced
echo constant('namespacename\constname'), "\n"; // also prints namespaced

문자열 안에서 네임스페이스 이름을 이스케이프하는 방법을 꼭 한번 읽어 두세요 (FAQ의 "동적 네임스페이스 이름" 항목 참고).

네임스페이스 키워드와 __NAMESPACE__ 매직 상수

PHP는 현재 네임스페이스 안의 요소에 추상적으로 접근하는 두 가지 방법을 지원해요. 바로 __NAMESPACE__ 매직 상수와 namespace 키워드예요.

__NAMESPACE__의 값은 현재 네임스페이스 이름을 담은 문자열이에요. 전역(네임스페이스 없는) 코드에서는 빈 문자열을 담고 있어요.

예제 1 — __NAMESPACE__ 예시, 네임스페이스 코드

<?php
namespace MyProject;

echo '"', __NAMESPACE__, '"'; // outputs "MyProject"

예제 2 — __NAMESPACE__ 예시, 전역 코드

<?php
echo '"', __NAMESPACE__, '"'; // outputs ""

__NAMESPACE__ 상수는 이름을 동적으로 만들 때 유용해요. 예를 들면:

예제 3 — 동적 이름 생성을 위한 __NAMESPACE__ 사용

<?php
namespace MyProject;

function get($classname)
{
    $a = __NAMESPACE__ . '\\' . $classname;
    return new $a;
}

namespace 키워드는 현재 네임스페이스나 하위 네임스페이스에서 요소를 명시적으로 요청할 때 쓸 수 있어요. 클래스의 self 연산자에 해당하는, 네임스페이스 버전이라고 보면 돼요.

예제 4 — 네임스페이스 안에서의 namespace 연산자

<?php
namespace MyProject;

use blah\blah as mine; // see "Using namespaces: Aliasing/Importing"

blah\mine(); // calls function MyProject\blah\mine()
namespace\blah\mine(); // calls function MyProject\blah\mine()

namespace\func(); // calls function MyProject\func()
namespace\sub\func(); // calls function MyProject\sub\func()
namespace\cname::method(); // calls static method "method" of class MyProject\cname
$a = new namespace\sub\cname(); // instantiates object of class MyProject\sub\cname
$b = namespace\CONSTANT; // assigns value of constant MyProject\CONSTANT to $b

예제 5 — 전역 코드에서의 namespace 연산자

<?php

namespace\func(); // calls function func()
namespace\sub\func(); // calls function sub\func()
namespace\cname::method(); // calls static method "method" of class cname
$a = new namespace\sub\cname(); // instantiates object of class sub\cname
$b = namespace\CONSTANT; // assigns value of constant CONSTANT to $b

네임스페이스 사용: 별칭/임포트 (Aliasing/Importing)

외부의 완전히 정규화된 이름을 별칭(alias)으로 참조하는 능력, 즉 임포트는 네임스페이스의 중요한 기능이에요. 유닉스 계열 파일시스템이 파일이나 디렉터리에 심볼릭 링크를 만들 수 있는 것과 비슷하죠.

PHP는 상수, 함수, 클래스, 인터페이스, 트레이트, enum, 그리고 네임스페이스 자체를 별칭/임포트할 수 있어요.

별칭은 use 연산자로 수행해요. 다섯 가지 종류의 임포트를 모두 보여주는 예제예요.

예제 1 — use 연산자로 임포트/별칭

<?php
namespace foo;
use My\Full\Classname as Another;

// this is the same as use My\Full\NSname as NSname
use My\Full\NSname;

// importing a global class
use ArrayObject;

// importing a function
use function My\Full\functionName;

// aliasing a function
use function My\Full\functionName as func;

// importing a constant
use const My\Full\CONSTANT;

$obj = new namespace\Another; // instantiates object of class foo\Another
$obj = new Another; // instantiates object of class My\Full\Classname
NSname\subns\func(); // calls function My\Full\NSname\subns\func
$a = new ArrayObject(array(1)); // instantiates object of class ArrayObject
// without the "use ArrayObject" we would instantiate an object of class foo\ArrayObject
func(); // calls function My\Full\functionName
echo CONSTANT; // echoes the value of My\Full\CONSTANT

네임스페이스 구분자를 포함하는 네임스페이스 이름(전역 이름인 FooBar와 달리 Foo\Bar 같은 이름)에서는 맨 앞 백슬래시가 필요 없고 쓰지도 않는 걸 권장해요. 임포트 이름은 반드시 완전히 정규화되어야 하고, 현재 네임스페이스를 기준으로 처리되지 않기 때문이에요.

PHP는 여러 use 문을 같은 줄에 두는 편리한 단축 표기도 지원해요.

예제 2 — use 연산자로 임포트/별칭, 여러 use 문 결합

<?php
use My\Full\Classname as Another, My\Full\NSname;

$obj = new Another; // instantiates object of class My\Full\Classname
NSname\subns\func(); // calls function My\Full\NSname\subns\func

임포트는 컴파일 시점에 수행되기 때문에 동적 클래스·함수·상수 이름에는 영향을 주지 않아요.

예제 3 — 임포트와 동적 이름

<?php
use My\Full\Classname as Another, My\Full\NSname;

$obj = new Another; // instantiates object of class My\Full\Classname
$a = 'Another';
$obj = new $a;      // instantiates object of class Another

게다가 임포트는 비정규화 이름과 정규화 이름에만 영향을 줘요. 완전히 정규화된 이름은 절대적이라 임포트의 영향을 받지 않아요.

예제 4 — 임포트와 완전히 정규화된 이름

<?php
use My\Full\Classname as Another, My\Full\NSname;

$obj = new Another; // instantiates object of class My\Full\Classname
$obj = new \Another; // instantiates object of class Another
$obj = new Another\thing; // instantiates object of class My\Full\Classname\thing
$obj = new \Another\thing; // instantiates object of class Another\thing

임포트의 스코프 규칙

use 키워드는 파일의 가장 바깥 스코프(전역 스코프) 또는 네임스페이스 선언 안에서 선언해야 해요. 임포트가 런타임이 아니라 컴파일 시점에 이루어지기 때문에 블록 스코프를 가질 수 없기 때문이에요. 다음 예제는 use 키워드의 잘못된 사용을 보여줘요.

예제 5 — 잘못된 임포트 규칙

<?php
namespace Languages;

function toGreenlandic()
{
    use Languages\Danish;

    // ...
}

임포트 규칙은 파일 단위예요. 즉 include된 파일은 부모 파일의 임포트 규칙을 상속받지 않아요.

그룹 use 선언

같은 네임스페이스에서 임포트하는 클래스·함수·상수는 하나의 use 문으로 묶을 수 있어요.

<?php

use some\namespace\ClassA;
use some\namespace\ClassB;
use some\namespace\ClassC as C;

use function some\namespace\fn_a;
use function some\namespace\fn_b;
use function some\namespace\fn_c;

use const some\namespace\ConstA;
use const some\namespace\ConstB;
use const some\namespace\ConstC;

// is equivalent to the following grouped use declaration
use some\namespace\{ClassA, ClassB, ClassC as C};
use function some\namespace\{fn_a, fn_b, fn_c};
use const some\namespace\{ConstA, ConstB, ConstC};

전역 공간 (Global space)

네임스페이스 정의가 없으면 모든 클래스와 함수 정의는 전역 공간에 놓여요 — 네임스페이스가 지원되기 전 PHP가 그랬던 것처럼요. 이름 앞에 \를 붙이면 네임스페이스 문맥 안에서도 그 이름이 전역 공간에서 요구된다는 것을 지정해요.

예제 1 — 전역 공간 지정 사용

<?php
namespace A\B\C;

/* This function is A\B\C\fopen */
function fopen() {
     /* ... */
     $f = \fopen(...); // call global fopen
     return $f;
}

함수와 상수의 전역 공간으로의 폴백

네임스페이스 안에서 PHP가 클래스 이름·함수·상수 문맥에서 비정규화 이름을 만나면 그것들을 서로 다른 우선순위로 해석해요. 클래스 이름은 항상 현재 네임스페이스 이름으로 풀려요. 그래서 내부 클래스나 네임스페이스 없는 사용자 클래스에 접근하려면 아래처럼 완전히 정규화된 이름으로 참조해야 해요.

예제 1 — 네임스페이스 안에서 전역 클래스에 접근하기

<?php
namespace A\B\C;
class Exception extends \Exception {}

$a = new Exception('hi'); // $a is an object of class A\B\C\Exception
var_dump($a::class);
$b = new \Exception('hi'); // $b is an object of class Exception
var_dump($b::class);

$c = new ArrayObject; // fatal error, class A\B\C\ArrayObject not found

함수와 상수의 경우, 네임스페이스 함수나 상수가 존재하지 않으면 PHP는 전역 함수나 상수로 폴백해요.

예제 2 — 네임스페이스 안의 전역 함수/상수 폴백

<?php
namespace A\B\C;

const E_ERROR = 45;
function strlen($str)
{
    return \strlen($str) - 1;
}

echo E_ERROR, "\n"; // prints "45"
echo INI_ALL, "\n"; // prints "7" - falls back to global INI_ALL

echo strlen('hi'), "\n"; // prints "1"
if (is_array('hi')) { // prints "is not array"
    echo "is array\n";
} else {
    echo "is not array\n";
}

이름 해석 규칙 (Name resolution rules)

해석 규칙을 위해 먼저 몇 가지 중요한 정의를 볼게요.

네임스페이스 이름 정의

  • 비정규화 이름(Unqualified name): 네임스페이스 구분자가 없는 식별자예요. Foo 같은 것.
  • 정규화 이름(Qualified name): 네임스페이스 구분자가 있는 식별자예요. Foo\Bar 같은 것.
  • 완전히 정규화된 이름(Fully qualified name): 네임스페이스 구분자로 시작하는 식별자예요. \Foo\Bar 같은 것. 네임스페이스 \Foo도 완전히 정규화된 이름이에요.
  • 상대 이름(Relative name): namespace로 시작하는 식별자예요. namespace\Foo\Bar 같은 것.

이름은 다음 해석 규칙을 따라 풀려요.

  • 완전히 정규화된 이름은 항상 맨 앞 네임스페이스 구분자를 뗀 이름으로 풀려요. 예를 들어 \A\BA\B로 풀려요.
  • 상대 이름은 항상 namespace를 현재 네임스페이스로 바꾼 이름으로 풀려요. 이름이 전역 네임스페이스에 있으면 namespace\ 접두사는 잘려요. 예를 들어 네임스페이스 X\Y 안의 namespace\AX\Y\A로 풀리고, 전역 네임스페이스 안의 같은 이름은 A로 풀려요.
  • 정규화 이름의 첫 구간은 현재 클래스/네임스페이스 임포트 테이블에 따라 변환돼요. 예를 들어 네임스페이스 A\B\CC로 임포트됐다면, 이름 C\D\EA\B\C\D\E로 변환돼요.
  • 정규화 이름에 적용되는 임포트 규칙이 없다면 현재 네임스페이스가 이름 앞에 붙어요. 예를 들어 네임스페이스 A\B 안의 C\D\EA\B\C\D\E로 풀려요.
  • 비정규화 이름은 해당 심볼 타입의 현재 임포트 테이블에 따라 변환돼요. 클래스류 이름은 클래스/네임스페이스 임포트 테이블, 함수 이름은 함수 임포트 테이블, 상수는 상수 임포트 테이블에 따라 변환된다는 뜻이에요. 예를 들어 use A\B\C; 다음에 new C()라고 쓰면 이름 A\B\C로 풀려요. 마찬가지로 use function A\B\foo; 다음에 foo()라고 쓰면 A\B\foo로 풀려요.
  • 비정규화 이름에 적용되는 임포트 규칙이 없고 그 이름이 클래스류 심볼을 가리키면 현재 네임스페이스가 앞에 붙어요. 예를 들어 네임스페이스 A\B 안의 new C()A\B\C로 풀려요.
  • 비정규화 이름에 적용되는 임포트 규칙이 없고 그 이름이 함수나 상수를 가리키며 코드가 전역 네임스페이스 밖에 있다면, 그 이름은 런타임에 해석돼요. 코드가 네임스페이스 A\B에 있다고 할 때 함수 foo() 호출은 이렇게 풀려요.
    • 먼저 현재 네임스페이스의 함수를 찾아요: A\B\foo().
    • 없으면 전역 함수 foo()를 찾아 호출해요.
<?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"

FAQ: 네임스페이스에 대해 알아야 할 것들

이 FAQ는 두 부분으로 나뉘어요. 흔한 질문과, 온전히 이해하면 도움이 되는 구현의 세부 사항이에요.

먼저 흔한 질문들부터 볼게요.

  • 네임스페이스를 안 쓰면 이런 거에 신경 써야 하나요?
  • 네임스페이스 안에서 내부/전역 클래스를 어떻게 쓰나요?
  • 네임스페이스 안에서 자신의 네임스페이스의 클래스·함수·상수를 어떻게 쓰나요?
  • \my\name이나 \name 같은 이름은 어떻게 풀리나요?
  • my\name 같은 이름은 어떻게 풀리나요?
  • name 같은 비정규화 클래스 이름은 어떻게 풀리나요?
  • name 같은 비정규화 함수 이름이나 비정규화 상수 이름은 어떻게 풀리나요?

그리고 네임스페이스 구현을 온전히 이해하는 데 도움이 되는 세부 사항 몇 가지가 있어요.

  • 임포트 이름은 같은 파일에 정의된 클래스와 충돌하면 안 돼요.
  • 중첩 네임스페이스는 허용되지 않아요.
  • 동적 네임스페이스 이름(따옴표로 감싼 식별자)은 백슬래시를 이스케이프해야 해요.
  • 어떤 백슬래시든 사용해 참조하는 정의되지 않은 상수는 치명적 오류로 죽어요.
  • 특수 상수 null, true, false는 재정의할 수 없어요.

네임스페이스를 안 쓰면 이런 거에 신경 써야 하나요?

아니요. 네임스페이스는 기존 코드에 어떤 영향도 주지 않고, 아직 쓰지 않은 네임스페이스 없는 미래의 코드에도 영향을 주지 않아요. 원한다면 이런 코드를 써도 돼요:

예제 1 — 네임스페이스 밖에서 전역 클래스에 접근하기

<?php
$a = new \stdClass;
print $a::class;

이것은 기능적으로 아래와 동등해요.

예제 2 — 네임스페이스 밖에서 전역 클래스에 접근하기

<?php
$a = new stdClass;
print $a::class;

네임스페이스 안에서 내부/전역 클래스를 어떻게 쓰나요?

예제 3 — 네임스페이스 안에서 내부 클래스에 접근하기

<?php
namespace foo;
$a = new \stdClass;
print $a::class . "\n";

function test(?\ArrayObject $parameter_type_example = null) {}

$a = \FilesystemIterator::CURRENT_AS_FILEINFO;
print $a . "\n";

// extending an internal or global class
class MyException extends \Exception {}
print MyException::class . "\n";

네임스페이스 안에서 자신의 네임스페이스의 클래스·함수·상수를 어떻게 쓰나요?

예제 4 — 네임스페이스 안에서 내부 클래스·함수·상수에 접근하기

<?php
namespace foo;

class MyClass {}

// using a class from the current namespace as a parameter type
function test(?MyClass $parameter_type_example = null) {}
// another way to use a class from the current namespace as a parameter type
function test2(?\foo\MyClass $parameter_type_example = null) {}

// extending a class from the current namespace
class Extended extends MyClass {}
print Extended::class . "\n";

// accessing a global function
$a = \strlen("test");
print $a . "\n";

// accessing a global constant
$b = \INI_ALL;
print $b . "\n";

\my\name이나 \name 같은 이름은 어떻게 풀리나요?

\로 시작하는 이름은 항상 보이는 그대로 풀려요. 그래서 \my\name은 실제로 my\name이고, \ExceptionException이에요.

예제 5 — 완전히 정규화된 이름

<?php
namespace foo;
$a = new \my\name(); // instantiates "my\name" class
echo \strlen('hi'); // calls function "strlen"
$a = \INI_ALL; // $a is set to the value of constant "INI_ALL"

my\name 같은 이름은 어떻게 풀리나요?

백슬래시를 포함하지만 백슬래시로 시작하지 않는 my\name 같은 이름은 두 가지 방식으로 풀릴 수 있어요.

my를 다른 이름으로 별칭하는 임포트 문이 있다면, 그 임포트 별칭이 my\namemy에 적용돼요.

그렇지 않으면 현재 네임스페이스 이름이 my\name 앞에 붙어요.

예제 6 — 정규화 이름

<?php
namespace foo;
use blah\blah as foo;

$a = new my\name(); // instantiates "foo\my\name" class
foo\bar::name(); // calls static method "name" in class "blah\blah\bar"
my\bar(); // calls function "foo\my\bar"
$a = my\BAR; // sets $a to the value of constant "foo\my\BAR"

name 같은 비정규화 클래스 이름은 어떻게 풀리나요?

백슬래시를 포함하지 않는 name 같은 클래스 이름은 두 가지 방식으로 풀릴 수 있어요.

name을 다른 이름으로 별칭하는 임포트 문이 있다면 그 임포트 별칭이 적용돼요.

그렇지 않으면 현재 네임스페이스 이름이 name 앞에 붙어요.

예제 7 — 비정규화 클래스 이름

<?php
namespace foo;
use blah\blah as foo;

$a = new name(); // instantiates "foo\name" class
foo::name(); // calls static method "name" in class "blah\blah"

name 같은 비정규화 함수 이름이나 비정규화 상수 이름은 어떻게 풀리나요?

백슬래시를 포함하지 않는 name 같은 함수나 상수 이름은 두 가지 방식으로 풀릴 수 있어요.

먼저 현재 네임스페이스 이름이 name 앞에 붙어요.

마지막으로, 상수나 함수 name이 현재 네임스페이스에 존재하지 않으면, 전역 상수나 함수 name이 존재할 때 그걸 사용해요.

예제 8 — 비정규화 함수 또는 상수 이름

<?php
namespace foo;
use blah\blah as foo;

const FOO = 1;

function my() {
    print __FUNCTION__ . "\n";
}
function foo() {
    print __FUNCTION__ . "\n";
}
function sort(&$a)
{
    \sort($a); // calls the global function "sort"
    $a = array_flip($a);
    return $a;
}

my(); // calls "foo\my"
print strlen('hi') . "\n"; // calls global function "strlen" because "foo\strlen" does not exist
$arr = array(1,3,2);
$b = sort($arr); // calls function "foo\sort"
var_dump($b);
foo(); // calls function "foo\foo" - import is not applied

$a = FOO; // sets $a to value of constant "foo\FOO" - import is not applied
print $a;
$b = INI_ALL; // sets $b to value of global constant "INI_ALL"
print $b;

임포트 이름은 같은 파일에 정의된 클래스와 충돌하면 안 돼요.

다음 스크립트 조합은 합법이에요.

file1.php

<?php
namespace my\stuff;
class MyClass {}

another.php

<?php
namespace another;
class thing {}

file2.php

<?php
namespace my\stuff;
include 'file1.php';
include 'another.php';

use another\thing as MyClass;
$a = new MyClass; // instantiates class "thing" from namespace another

클래스 MyClassmy\stuff 네임스페이스 안에 존재하더라도 이름 충돌이 없어요. MyClass 정의가 별도의 파일에 있기 때문이죠. 하지만 다음 예제는 MyClass가 use 문과 같은 파일에 정의되어 있어서 이름 충돌로 치명적 오류가 나요.

<?php
namespace my\stuff;
use another\thing as MyClass;
class MyClass {} // fatal error: MyClass conflicts with import statement
$a = new MyClass;

중첩 네임스페이스는 허용되지 않아요.

PHP는 네임스페이스 중첩을 허용하지 않아요.

<?php
namespace my\stuff {
    namespace nested {
        class foo {}
    }
}

하지만 아래처럼 하면 중첩 네임스페이스를 쉽게 흉내 낼 수 있어요.

<?php
namespace my\stuff\nested {
    class foo {}
}

동적 네임스페이스 이름(따옴표로 감싼 식별자)은 백슬래시를 이스케이프해야 해요.

백슬래시는 문자열 안에서 이스케이프 문자로 쓰이기 때문에 문자열 안에서 쓸 때는 항상 두 번 써야 한다는 걸 깨닫는 게 정말 중요해요. 그러지 않으면 의도하지 않은 결과가 나올 위험이 있어요:

예제 9 — 이중 따옴표 문자열 안에서 네임스페이스 이름을 쓰는 위험

<?php
$a = "dangerous\name"; // \n is a newline inside double quoted strings!
$obj = new $a;

$a = 'not\at\all\dangerous'; // no problems here.
$obj = new $a;

단일 따옴표 문자열 안에서는 백슬래시 이스케이프 시퀀스가 훨씬 안전하지만, 그럼에도 모든 문자열에서 백슬래시를 이스케이프하는 게 모범 사례로 권장돼요.

어떤 백슬래시든 사용해 참조하는 정의되지 않은 상수는 치명적 오류로 죽어요.

FOO 같은 비정규화 미정의 상수는 PHP가 FOO를 상수의 값으로 가정했다는 notice를 만들어요. 반면 백슬래시를 포함하는 어떤 (정규화든 완전히 정규화든) 상수가 발견되지 않으면 치명적 오류가 나요.

예제 10 — 정의되지 않은 상수

<?php
namespace bar;
$a = FOO; // produces notice - undefined constants "FOO" assumed "FOO";
$a = \FOO; // fatal error, undefined namespace constant FOO
$a = Bar\FOO; // fatal error, undefined namespace constant bar\Bar\FOO
$a = \Bar\FOO; // fatal error, undefined namespace constant Bar\FOO

특수 상수 null, true, false는 재정의할 수 없어요.

특수·내장 상수인 네임스페이스 상수를 정의하려는 어떤 시도든 치명적 오류를 만들어요.

예제 11 — 정의되지 않은 상수

<?php
namespace bar;
const NULL = 0; // fatal error;
const true = 'stupid'; // also fatal error;
// etc.