문제 상황
파일 1에는 네임스페이스 ns_1이, 파일 2에는 네임스페이스 ns_2가 정의되어 있다고 가정해 보겠습니다. 이 두 파일을 파일 3에서 포함(include)하더라도, ns_1과 ns_2 네임스페이스가 실제로 로드되었는지 직접 확인할 방법은 없습니다.
유일하게 가능한 접근 방식은 class_exists 함수를 활용하는 것이며, 특정 네임스페이스에 속한 클래스 목록은 get_declared_classes 함수를 통해 얻을 수 있습니다. 쉽게 말해, 선언된 모든 클래스 이름을 순회하면서 해당 네임스페이스와 일치하는 항목을 찾아내는 방식입니다.
참고로 PHP에는 get_declared_classes()처럼 네임스페이스 전체를 직접 조회해 주는 내장 함수는 제공되지 않습니다. 따라서 아래처럼 선언된 클래스 이름을 역으로 분석하는 방법이 사실상 표준적인 우회 방법으로 사용됩니다.
방법 1: namespaceExists 함수 직접 작성
선언된 클래스 목록을 순회하며 특정 네임스페이스로 시작하는 클래스가 존재하는지 확인하는 함수를 만들 수 있습니다.
function namespaceExists($namespace) {
$namespace .= "\\";
foreach(get_declared_classes() as $name)
if(strpos($name, $namespace) === 0) return true;
return false;
}
방법 2: 정규 표현식으로 네임스페이스 추출
정규 표현식을 활용하면 선언된 클래스 이름에서 네임스페이스 부분만 추출하여, 하위 네임스페이스까지 계층 구조로 정리할 수도 있습니다.
예제
<?php
namespace FirstNamespace;
class new_class {}
namespace SecondNamespace;
class new_class {}
namespace ThirdNamespace\FirstSubNamespace;
class new_class {}
namespace ThirdNamespace\SecondSubNamespace;
class new_class {}
namespace SecondNamespace\FirstSubNamespace;
class new_class {}
$namespaces=array();
foreach(get_declared_classes() as $name) {
if(preg_match_all("@[^\\\]+(?=\\\)@iU", $name, $matches)) {
$matches = $matches[0];
$parent =&$namespaces;
while(count($matches)) {
$match = array_shift($matches);
if(!isset($parent[$match]) && count($matches))
$parent[$match] = array();
$parent =&$parent[$match];
}
}
}
print_r($namespaces);
출력 결과
위 코드를 실행하면 다음과 같은 결과가 출력됩니다.
Array ( [FirstNamespace] => [SecondNamespace] => Array ( [FirstSubNamespace] => ) [ThirdNamespace] => Array ( [FirstSubNamespace] => [SecondSubNamespace] => ) )
코드 설명
위 예제에서는 여러 개의 네임스페이스(FirstNamespace, SecondNamespace 등)를 생성하고 각각 빈 클래스(new_class)를 선언합니다. 이후 네임스페이스 정보를 담을 배열을 만들고, foreach 루프를 통해 현재 환경에서 선언된 모든 클래스를 하나씩 순회합니다.
각 클래스 이름에 정규 표현식 매칭을 적용해 네임스페이스 부분만 추출하고, 이를 중첩 배열 형태로 조립합니다. 그 결과, 해당 환경에서 정의된 모든 네임스페이스가 상위·하위 계층 구조로 깔끔하게 정리되어 출력됩니다.