다음 3개 SPL 클래스를 활용하면 된다.
RecursiveIteratorIterator
RecursiveDirectoryIterator
CallbackFilterIterator
예컨대 특정 경로 아래 모든 .php 파일 갯수를 세려면 어떻게 해야 될까?
차선책을 먼저 보고, 그 다음 최선책을 보기로 한다.
전통적으로 PHP 커뮤니티는 이 문제를 이런 식으로 접근했다.
// variated from: https://stackoverflow.com/a/24784144
function countPhpInDir($dir) {
$count = 0;
$files = scandir($dir);
foreach ($files as $key => $value) {
$path = realpath($dir . DIRECTORY_SEPARATOR . $value);
if (!is_dir($path) && substr($path, -4) === '.php') {
$count++;
} else if ($value != "." && $value != "..") {
$count += countPhpInDir($path);
}
}
return $count;
}
echo "total " . countPhpInDir(경로) . " PHP files";
'접근'이라기엔 지나치게 문자적인 해결이다. 그냥 재귀함수 삽 하나를 들고 폴더로 들어가서, 일일이 삽질해 퍼내 가며 직접 찾는다.
창피할 정도로 단순 무식한 구현이지만, PHP 업계는 꽤 오랫동안 이런 수제 코드 조각들로 이 문제를 해결해 왔다. 어쨌든 굴러는 가니까.
상기 코드와 똑같은 것을, 다음과 같이 2가지 클래스만으로 구현할 수 있다.
$dir = new RecursiveDirectoryIterator(경로);
$files = new RecursiveIteratorIterator($dir);
$count = 0;
foreach ($files as $file) {
if ($file->getExtension() === 'php') {
$count++;
}
}
echo "total $count PHP files";
확실히 차선책보다는 낫다.
SplFileInfo
클래스를 쓸 수 있어서 매우 편리하다.그런데 괜한 욕심이 더 난다. 저 foreach
루프는 최선일까? 나는 단지 PHP 파일만 골라서 보고 싶은 거 아닌가? 더 깨끗하고 논리적인 방법이 없을까?
이때 3번째 클래스를 사용한다.
$dir = new RecursiveDirectoryIterator(경로);
$php = new RecursiveCallbackFilterIterator($dir, function ($current, $key, $iterator) {
return $iterator->hasChildren() // $current가 Iterator라면 pass
|| $current->getExtension() === 'php';
});
$files = new RecursiveIteratorIterator($php);
$count = iterator_count($files);
echo "total $count PHP files";
아까보다 좀더 낫다.
iterator_count()
하는 코드로 바뀌어 있다. (적어도 겉으로는) 실제로 foreach
순회를 할 필요가 없다.$php
반복자만 따로 라이브러리로 분리해서 재사용하는 것이 가능할 것이다.사용된 SPL 클래스 3가지를 간단히 해설하자면 다음과 같다.
CallbackFilterIterator
RecursiveDirectoryIterator
SplFileInfo
를 yield
한다.RecursiveDirectoryIterator
를 yield
한다.RecursiveIteratorIterator
이론이 좀 어렵다면 그냥 위의 모범답안 2개를 눈과 손에 익혀 두면 된다.
흔히들 PHP를 생각할 때 foreach if else
루프 지옥과 날코딩의 짬뽕으로 "어쨌든 굴러는 가는" 구현체를 생각한다.
하지만 그건 그간의 PHP 사용 관행이었을 뿐이고, 알고 보면, Standard PHP Library는 생각보다 방대하다.
public hasNext(): bool
메소드가 핵심인 CachingIterator 같은 것도 있다.SPL이 아닌 '기본 내장 구현체'나 일반적으로 동봉되는 extensions들까지 합치면, 쓸모 있는 기성 구현체는 더 많다.
오늘날에 PHP 엔지니어 명함을 내밀고 다니자면 이 정도는 가급적 학습해서 활용하려고 해야 할 것이다. 바퀴를 다시 만들 필요는 없고, 어떤 바퀴들은 우리가 그간 찾지 않았다 뿐이지 실은 분명 거기 있었기 때문이다. 우리가 그간 그 바퀴들을 찾으려 하지 않았던 건, 어쩌면 바퀴를 다시 만드는 비용이 너무 싼 나머지, 각자 허술한 바퀴를 너무 자주 너무 많이 너무 오랫동안 만드는 데 안주해 버렸기 때문인지도 모른다. 그 바퀴들도 구르기야 하겠지만, 기왕이면 그런 엔지니어링 말고, 주어진 언어의 사양을 활용할 줄 아는 엔지니어링을 하려고 하자.
php 5 에서는 spl 이 scandir , glob 이런거 보다 느려서 안썻죠.
spl 배열같은것도 기본배열보다 느리고 구현도 이상하고
spl 패키지들 몽땅싸잡아서 끔찍하다고 욕도먹고... ㅠ
php 7~8 사이에 개선이 있어서 괜찮은거같은데 기존코드를 엎진않아서 쓰는데는
많이 못봤어요.