이 세상에 하나는 남기고 가자

세상에 필요한 소스코드 한줄 남기고 가자

PHP CLI 사용자 입력(STDIN) 받기

아사마루

PHP CLI를 사용해서 Shell Script를 생성하다보면 사용자에게 어떤 데이터를 입력 받아야 상황이 발생할 수 있다. 이런 경우에 사용할 수 있는 몇가지 방법에 대해 설명하고자 한다.

우선 PHP 메뉴얼 중 Input/output streams에 있는 예시에 약간의 코드를 더해서 설명하겠다.

<?php
$stdin = fopen('php://stdin', 'r');
$fileContents = fread($stdin, 1024);
fclose($stdin);

echo $fileContents;

우선 위 방법은 fopen을 사용해서 사용자에게 입력 받는 방법이다. 이 방법은 사용자의 입력이 완료될 때(Enter 입력시)까지 대기하게 하거나 pipe(|)를 이용해 입력이 가능하다. 위 내용을 fopenTest.php로 저장했다면 아래와 같이 실행해 볼 수 있다. 그리고 1024는 입력의 최대 길이로 그 이상의 입력이 들어오면 무시된다.

$ php fopenTest.php
test # 입력 후 Enter
test

$ echo "test" | php fopenTest.php
test

이와 동일한 역할로 다음과 같이 사용할 수도 있다.

<?php
echo trim(fgets(STDIN)); // reads one line from STDIN

처음 코드와 동일하게 동작한다. 그리고 특정 형식으로 데이터를 입력받고 싶다면 아래와 같이 사용할 수도 있다.

<?php
fscanf(STDIN, "%d\n", $number); // reads number from STDIN
echo $number;

위 코드는 실행 결과는 약간 다른점이 있으니 아래의 실행 예시를 참고하자.

$ php fopenTest.php
test # 입력 후 Enter
# 입력값이 무시되어 아무값도 출력되지 않음

$ php fopenTest.php
2test3 # 입력 후 Enter
2 # 2 이후의 입력값이 무시됨

$ php fopenTest.php
123 # 입력 후 Enter
123 # 유효한 입력으로 123이 출력됨

$ echo "123" | php fopenTest.php
123 # 유효한 입력으로 123이 출력됨

PHP에서는 다른 방법으로 readline을 사용하는 것이 가능하다. 예를들면 아래와 같이 확인해 볼 수 있다.

<?php
//get 3 commands from user
for ($i=0; $i < 3; $i++) {
        $line = readline("Command: ");
        readline_add_history($line);
}

//dump history
print_r(readline_list_history());

//dump variables
print_r(readline_info());

그런데 이 함수는 제약조건이 있다. 메뉴얼에 따르면 readline_list_historylibreadline과 함께 컴파일되어야만 사용할 수 있다고 되어 있다. 그래서 위 예시는 제대로 테스트 해보지 못했다.


그렇다면 사용자에게 한글자의 입력만 받고 싶다면 어떻게하면 될까? 아래의 예시를 보자.

<?php
echo '변경 후에는 복구할 수 없습니다. 변경하시겠습니까? (y/n) ';
$input = fgetc(STDIN);

위 코드를 실행하면 한글자만 입력을 받게 되지만 한글자를 입력하고 Enter를 눌러야 한다. 그럼 그냥 한글자를 입력하면 바로 입력을 완료하고자 한다면 어떻게 할까? 아래에 그 답이 있다.

<?php
echo '변경 후에는 복구할 수 없습니다. 변경하시겠습니까? (y/n) ';

readline_callback_handler_install('', function () {
});
while (true) {
  $r = [STDIN];
  $w = null;
  $e = null;
  $n = stream_select($r, $w, $e, 0);
  if ($n && in_array(STDIN, $r)) {
    $c = stream_get_contents(STDIN, 1);
    if (strcasecmp($c, 'y') != 0) {
      echo "\n작업을 취소함\n";
      exit;
    }
    break;
  }
}

echo "변경 완료\n";

나의 경우엔 위 스크립트를 사용해야하는 상황이 가장 많은 것 같다. 위험한 동작을 실행하기전 다시 한번 확인하는 것이 정신 건강에 이롭다.

Comment