' . htmlspecialchars(lang($messageKey)) . '
';
return <<
';
for ($i = 0; $i < $totalSteps; $i++) {
$class = 'progress-step';
if ($i < $currentStep) {
$class .= ' completed';
} elseif ($i === $currentStep) {
$class .= ' active';
}
$html .= '
';
}
$html .= '
';
return $html;
}
/**
* 현재 PHP 프로세스를 실행 중인 웹 서버 사용자명을 감지합니다.
*
* posix 확장 → exec('whoami') → 환경변수 순서로 시도하며,
* 모두 실패하면 null을 반환합니다.
*
* @return string|null 웹 서버 사용자명 (예: 'www-data', 'nginx', 'apache') 또는 null
*/
function getWebServerUser(): ?string
{
// 1. posix 확장 (가장 신뢰도 높음)
if (function_exists('posix_geteuid') && function_exists('posix_getpwuid')) {
$info = posix_getpwuid(posix_geteuid());
if ($info && !empty($info['name'])) {
return $info['name'];
}
}
// 2. exec('whoami')
if (function_exists('exec')) {
$user = @exec('whoami');
if (!empty($user)) {
return trim($user);
}
}
// 3. 환경변수
$envUser = getenv('USER') ?: getenv('APACHE_RUN_USER') ?: null;
if (!empty($envUser)) {
return $envUser;
}
return null;
}
/**
* 현재 PHP 프로세스를 실행 중인 웹 서버 그룹명을 감지합니다.
*
* @return string|null 웹 서버 그룹명 (예: 'www-data', 'nginx') 또는 null
*/
function getWebServerGroup(): ?string
{
if (function_exists('posix_getegid') && function_exists('posix_getgrgid')) {
$info = posix_getgrgid(posix_getegid());
if ($info && !empty($info['name'])) {
return $info['name'];
}
}
$envGroup = getenv('APACHE_RUN_GROUP') ?: null;
if (!empty($envGroup)) {
return $envGroup;
}
return null;
}
/**
* 파일/디렉토리의 소유자 이름을 반환합니다.
*
* posix 확장이 없거나 확인할 수 없는 경우 null을 반환합니다.
*
* @param string $path 파일/디렉토리 경로
* @return string|null 소유자 이름 또는 null
*/
function getFileOwnerName(string $path): ?string
{
$uid = @fileowner($path);
if ($uid === false) {
return null;
}
if (function_exists('posix_getpwuid')) {
$info = @posix_getpwuid($uid);
if ($info && !empty($info['name'])) {
return $info['name'];
}
}
// posix 확장이 없으면 UID를 문자열로 반환
return (string) $uid;
}
/**
* 파일/디렉토리의 소유 그룹 이름을 반환합니다.
*
* posix 확장이 없거나 확인할 수 없는 경우 null을 반환합니다.
*
* @param string $path 파일/디렉토리 경로
* @return string|null 그룹 이름 또는 null
*/
function getFileGroupName(string $path): ?string
{
$gid = @filegroup($path);
if ($gid === false) {
return null;
}
if (function_exists('posix_getgrgid')) {
$info = @posix_getgrgid($gid);
if ($info && !empty($info['name'])) {
return $info['name'];
}
}
// posix 확장이 없으면 GID를 문자열로 반환
return (string) $gid;
}
/**
* chown 명령어에 사용할 user:group 문자열을 반환합니다.
*
* 감지 실패 시 'www-data:www-data'를 기본값으로 사용합니다.
*
* @return string 'user:group' 형식 문자열
*/
function getChownTarget(): string
{
$user = getWebServerUser() ?? 'www-data';
$group = getWebServerGroup() ?? $user;
return $user . ':' . $group;
}
/**
* 디렉토리 권한을 체크합니다.
*
* Step 0과 Step 2에서 재사용 가능한 공통 함수입니다.
* 디렉토리를 생성하지 않고 존재 여부와 권한만 체크합니다.
*
* @param array