문자열

문자열 (String)

string은 문자의 연속인데요, 여기서 문자 하나는 바이트 하나와 같아요. 즉 PHP는 256개의 문자 집합만 지원해서 기본적으로 유니코드를 지원하지 않아요. 자세한 내용은 문자열 타입의 세부 사항에서 다뤄요.

참고: 32비트 빌드에서는 string이 최대 2GB(2147483647바이트)까지 될 수 있어요.

문법 (Syntax)

string 리터럴은 네 가지 방식으로 지정할 수 있어요.

  • 작은따옴표 (single quoted)
  • 큰따옴표 (double quoted)
  • heredoc 구문
  • nowdoc 구문

작은따옴표 (Single quoted)

string을 지정하는 가장 간단한 방법은 작은따옴표(')로 감싸는 거예요.

리터럴 작은따옴표를 지정하려면 백슬래시(\)로 이스케이프하고, 리터럴 백슬래시를 지정하려면 두 개를 연속으로 써요(\\). 나머지 백슬래시는 모두 리터럴 백슬래시로 취급돼요. 즉 다른 곳에서 익숙해진 이스케이프 시퀀스들(예: \r이나 \n)도 특별한 의미 없이 지정된 그대로 출력돼요.

참고: 큰따옴표 구문이나 heredoc 구문과 달리, 작은따옴표로 감싼 string에서는 변수와 특수 문자 이스케이프 시퀀스가 확장되지 않아요.

예제 #1 문법 변형들

<?php
echo 'this is a simple string', PHP_EOL;

echo 'You can also have embedded newlines in
strings this way as it is
okay to do', PHP_EOL;

// Outputs: Arnold once said: "I'll be back"
echo 'Arnold once said: "I\'ll be back"', PHP_EOL;

// Outputs: You deleted C:\*.*?
echo 'You deleted C:\\*.*?', PHP_EOL;

// Outputs: You deleted C:\*.*?
echo 'You deleted C:\*.*?', PHP_EOL;

// Outputs: This will not expand: \n a newline
echo 'This will not expand: \n a newline', PHP_EOL;

// Outputs: Variables do not $expand $either
echo 'Variables do not $expand $either', PHP_EOL;
?>

큰따옴표 (Double quoted)

string을 큰따옴표(")로 감싸면 PHP는 특수 문자에 대해 다음 이스케이프 시퀀스들을 해석해요:

이스케이프된 문자들 | 시퀀스 | 의미 | |---|---| | \n | 라인피드 (LF, ASCII의 0x0A(10)) | | \r | 캐리지 리턴 (CR, ASCII의 0x0D(13)) | | \t | 가로 탭 (HT, ASCII의 0x09(9)) | | \v | 세로 탭 (VT, ASCII의 0x0B(11)) | | \e | 이스케이프 (ESC, ASCII의 0x1B(27)) | | \f | 폼 피드 (FF, ASCII의 0x0C(12)) | | \\ | 백슬래시 | | \$ | 달러 기호 | | \" | 큰따옴표 | | \[0-7]{1,3} | 8진수: 정규 표현식 [0-7]{1,3}에 일치하는 문자 시퀀스는 8진수 표기의 한 문자예요. (예: "\\101" === "A") 바이트에 맞게 조용히 넘치도록 처리돼요 (예: "\\400" === "\\000") | | \x[0-9A-Fa-f]{1,2} | 16진수: 정규 표현식 [0-9A-Fa-f]{1,2}에 일치하는 문자 시퀀스는 16진수 표기의 한 문자예요. (예: "\\x41" === "A") | | \u{[0-9A-Fa-f]+} | 유니코드: 정규 표현식 [0-9A-Fa-f]+에 일치하는 문자 시퀀스는 UTF-8로 인코딩된 유니코드 코드 포인트예요. |

작은따옴표 string과 마찬가지로, 다른 문자를 이스케이프하면 그 백슬래시도 그대로 출력돼요.

큰따옴표 string의 가장 중요한 특징은 변수 이름이 확장된다는 점이에요. 자세한 내용은 문자열 보간에서 다뤄요.

Heredoc

string을 구분하는 세 번째 방법이 heredoc 구문이에요: <<<. 이 연산자 뒤에 식별자를 제공하고 개행을 넣어요. 그다음 string 본문이 오고, 마지막에 같은 식별자로 다시 닫아요.

닫는 식별자는 공백이나 탭으로 들여쓰기할 수 있어요. 닫는 식별자의 들여쓰기와 같은 양의 공백이 heredoc 문자열의 모든 줄 앞에서 제거돼요. PHP 7.3.0 이전에는 닫는 식별자가 줄의 첫 칸에서 시작해야 했어요.

또한 닫는 식별자는 PHP의 다른 라벨과 같은 명명 규칙을 따라야 해요. 영숫자와 밑줄만 포함해야 하고, 숫자나 밑줄이 아닌 문자로 시작해야 해요.

예제 #2 PHP 7.3.0 기준 기본 Heredoc 예제

<?php
// closing identifier not indented
echo <<<END
      a
     b
    c
\n
END;

// closing identifier indented with 3 spaces; 3 spaces stripped from each line
echo <<<END
      a
     b
    c
   END;

PHP 7.3에서의 위 예제 출력:

      a
     b
    c

   a
  b
 c

닫는 식별자가 본문의 어떤 줄보다 더 들여쓰기되면 ParseError가 발생해요:

예제 #3 닫는 식별자는 본문의 어떤 줄보다 더 들여쓰기되면 안 됨

<?php
echo <<<END
  a
 b
c
   END;

PHP 7.3에서의 위 예제 출력:

Parse error: Invalid body indentation level (expecting an indentation level of at least 3) in example.php on line 4

닫는 식별자를 들여쓰기한다면 탭도 사용할 수 있어요. 다만 탭과 공백을 섞어 쓰면 안 돼요. 본문과 닫는 식별자의 들여쓰기는 둘 중 하나로 일관되게 사용해야 해요. 탭과 공백을 섞으면 ParseError가 발생해요. 이런 공백 제약이 있는 이유는 들여쓰기에 탭과 공백을 섞으면 가독성에 해가 되기 때문이에요.

예제 #4 본문과 닫는 식별자의 서로 다른 들여쓰기

<?php
// All of the following code does not work.

// different indentation for body (spaces) and ending marker (tabs)
{
    echo <<<END
     a
        END;
}

// mixing spaces and tabs in body
{
    echo <<<END
        a
     END;
}

// mixing spaces and tabs in ending marker
{
    echo <<<END
          a
         END;
}

PHP 7.3에서의 위 예제 출력:

Parse error: Invalid indentation - tabs and spaces cannot be mixed in example.php line 8

본문 문자열의 닫는 식별자 뒤에 세미콜론이나 개행이 꼭 필요한 건 아니에요. 예를 들어 PHP 7.3.0부터 다음 코드가 허용돼요:

예제 #5 닫는 식별자 뒤의 표현식 계속하기

<?php
$values = [<<<END
a
  b
    c
END, 'd e f'];
var_dump($values);

PHP 7.3에서의 위 예제 출력:

array(2) {
  [0] =>
  string(11) "a
  b
    c"
  [1] =>
  string(5) "d e f"
}

닫는 식별자가 줄의 시작에 있으면, 그것이 다른 단어의 일부인지와 상관없이 닫는 식별자로 간주되어 ParseError를 일으켜요.

예제 #6 문자열 본문의 닫는 식별자가 ParseError를 유발하는 경향이 있음

<?php
$values = [<<<END
a
b
END ING
END, 'd e f'];

PHP 7.3에서의 위 예제 출력:

Parse error: syntax error, unexpected identifier "ING", expecting "]" in example.php on line 5

이 문제를 피하려면 간단한 규칙을 따르는 게 안전해요: 본문 텍스트에 등장하는 단어를 닫는 식별자로 선택하지 말아요.

PHP 7.3.0 이전에는 닫는 식별자가 있는 줄에 세미콜론(;) 외의 다른 문자가 있어서는 안 된다는 점이 매우 중요해요. 특히 식별자를 들여쓰기하면 안 되고, 세미콜론 앞뒤에 공백이나 탭이 있어서도 안 돼요. 닫는 식별자 바로 앞의 첫 문자가 로컬 운영체제가 정의한 개행이어야 한다는 점도 알아둬야 해요. UNIX 시스템(macOS 포함)에서는 이것이 \n이에요. 닫는 구분자는 반드시 개행으로 이어져야 해요.

이 규칙을 어겨서 닫는 식별자가 "깨끗하지" 않으면 닫는 식별자로 인식되지 않고, PHP는 계속해서 찾아요. 현재 파일이 끝날 때까지 올바른 닫는 식별자를 찾지 못하면 마지막 줄에서 구문 오류가 발생해요.

예제 #7 PHP 7.3.0 이전의 유효하지 않은 예제

<?php
class foo {
    public $bar = <<<EOT
bar
    EOT;
}
// Identifier must not be indented
?>

예제 #8 PHP 7.3.0 이전에도 유효한 예제

<?php
class foo {
    public $bar = <<<EOT
bar
EOT;
}
?>

변수를 포함하는 heredoc은 클래스 속성 초기화에는 사용할 수 없어요.

heredoc 텍스트는 큰따옴표가 없는 큰따옴표 string처럼 동작해요. 즉 heredoc 안의 따옴표는 이스케이프할 필요가 없지만, 위에 나열한 이스케이프 코드는 여전히 사용할 수 있어요. 변수는 확장되지만, string에서 복잡한 변수를 표현할 때처럼 heredoc 안에서도 같은 주의가 필요해요.

예제 #9 Heredoc 문자열 따옴표 예제

<?php
$str = <<<EOD
Example of string
spanning multiple lines
using heredoc syntax.
EOD;

/* More complex example, with variables. */
class foo
{
    var $foo;
    var $bar;

    function __construct()
    {
        $this->foo = 'Foo';
        $this->bar = array('Bar1', 'Bar2', 'Bar3');
    }
}

$foo = new foo();
$name = 'MyName';

echo <<<EOT
My name is "$name". I am printing some $foo->foo.
Now, I am printing some {$foo->bar[1]}.
This should print a capital 'A': \x41
EOT;
?>

위 예제는 다음을 출력해요:

My name is "MyName". I am printing some Foo.
Now, I am printing some Bar2.
This should print a capital 'A': A

Heredoc 구문으로 함수 인자에 데이터를 전달하는 것도 가능해요:

예제 #10 인자에서의 Heredoc 예제

<?php
var_dump(array(<<<EOD
foobar!
EOD
));
?>

Heredoc 구문으로 정적 변수와 클래스 속성/상수를 초기화하는 것도 가능해요:

예제 #11 Heredoc으로 정적 값 초기화하기

<?php
// Static variables
function foo()
{
    static $bar = <<<LABEL
Nothing in here...
LABEL;
}

// Class properties/constants
class foo
{
    const BAR = <<<FOOBAR
Constant example
FOOBAR;

    public $baz = <<<FOOBAR
Property example
FOOBAR;
}
?>

여는 Heredoc 식별자는 선택적으로 큰따옴표로 감쌀 수 있어요:

예제 #12 Heredoc에서 큰따옴표 사용하기

<?php
echo <<<"FOOBAR"
Hello World!
FOOBAR;
?>

Nowdoc

nowdoc은 작은따옴표 문자열에 대한 heredoc과 같은 관계예요. nowdoc은 heredoc과 비슷하게 지정하지만, nowdoc 안에서는 문자열 보간이 전혀 일어나지 않아요. 이 구조는 PHP 코드나 다른 큰 텍스트 블록을 이스케이프 없이 임베드하기에 이상적이에요. 파싱 대상이 아닌 텍스트 블록을 선언한다는 점에서 SGML <![CDATA[ ]]> 구조와 공통점이 있어요.

nowdoc은 heredoc에 쓰는 것과 같은 <<< 시퀀스로 식별하지만, 뒤따르는 식별자는 작은따옴표로 감싸요(예: <<<'EOT'). heredoc 식별자의 모든 규칙이 nowdoc 식별자에도 적용되는데, 특히 닫는 식별자의 등장에 관한 규칙이 그래요.

예제 #13 Nowdoc 문자열 따옴표 예제

<?php
echo <<<'EOD'
Example of string spanning multiple lines
using nowdoc syntax. Backslashes are always treated literally,
e.g. \\ and \'.
EOD;

위 예제는 다음을 출력해요:

Example of string spanning multiple lines
using nowdoc syntax. Backslashes are always treated literally,
e.g. \\ and \'.

예제 #14 변수가 있는 Nowdoc 문자열 따옴표 예제

<?php
class foo
{
    public $foo;
    public $bar;

    function __construct()
    {
        $this->foo = 'Foo';
        $this->bar = array('Bar1', 'Bar2', 'Bar3');
    }
}

$foo = new foo();
$name = 'MyName';

echo <<<'EOT'
My name is "$name". I am printing some $foo->foo.
Now, I am printing some {$foo->bar[1]}.
This should not print a capital 'A': \x41
EOT;
?>

위 예제는 다음을 출력해요:

My name is "$name". I am printing some $foo->foo.
Now, I am printing some {$foo->bar[1]}.
This should not print a capital 'A': \x41

예제 #15 정적 데이터 예제

<?php
class foo {
    public $bar = <<<'EOT'
bar
EOT;
}
?>

문자열 보간 (String interpolation)

string을 큰따옴표나 heredoc으로 지정하면 그 안에서 변수를 치환할 수 있어요.

구문에는 기본 구문과 고급 구문 두 종류가 있어요. 기본 구문이 가장 흔하고 편리해요. 최소한의 노력으로 변수, array 값, object 속성을 string 안에 임베드할 수 있게 해줘요.

기본 구문

달러 기호($)를 만나면, 그 뒤에 따라오는 변수 이름으로 쓸 수 있는 문자들이 변수 이름으로 해석되어 치환돼요.

예제 #16 문자열 보간

<?php
$juice = "apple";

echo "He drank some $juice juice." . PHP_EOL;

?>

위 예제는 다음을 출력해요:

He drank some apple juice.

형식적으로 기본 변수 치환 구문의 구조는 다음과 같아요:

string-variable::
     variable-name   (offset-or-property)?
   | ${   expression   }

offset-or-property::
     offset-in-string
   | property-in-string

offset-in-string::
     [   name   ]
   | [   variable-name   ]
   | [   integer-literal   ]

property-in-string::
     ->  name

variable-name::
     $   name

name::
     [a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*

${ expression } 구문은 변수 변수(variable variables)로 해석될 수 있기 때문에 PHP 8.2.0부터 권장하지 않아요: <?php const foo = 'bar'; $foo = 'foo'; $bar = 'bar'; var_dump("${foo}"); var_dump("${(foo)}"); ?> PHP 8.2에서의 위 예제 출력: Deprecated: Using ${var} in strings is deprecated, use {$var} instead in file on line 6 Deprecated: Using ${expr} (variable variables) in strings is deprecated, use {${expr}} instead in file on line 9 string(3) "foo" string(3) "bar" 위 예제는 다음을 출력해요: string(3) "foo" string(3) "bar" 대신 고급 문자열 보간 구문을 사용해야 해요.

참고: 유효한 이름을 만들 수 없으면 달러 기호가 문자열에 그대로 남아요: <?php echo "No interpolation $ has happened\n"; echo "No interpolation $\n has happened\n"; echo "No interpolation $2 has happened\n"; ?> 위 예제는 다음을 출력해요: No interpolation $ has happened No interpolation $ has happened No interpolation $2 has happened

예제 #17 배열의 첫 차원 또는 속성 값 보간

<?php
$juices = array("apple", "orange", "string_key" => "purple");

echo "He drank some $juices[0] juice.";
echo PHP_EOL;
echo "He drank some $juices[1] juice.";
echo PHP_EOL;
echo "He drank some $juices[string_key] juice.";
echo PHP_EOL;

class A {
    public $s = "string";
}

$o = new A();

echo "Object value: $o->s.";
?>

위 예제는 다음을 출력해요:

He drank some apple juice.
He drank some orange juice.
He drank some purple juice.
Object value: string.

참고: 배열 키는 따옴표로 묶으면 안 돼요. 그래서 기본 구문으로는 상수를 키로 참조할 수 없어요. 대신 고급 구문을 사용하세요.

PHP 7.1.0부터 음수 숫자 인덱스도 지원해요.

예제 #18 음수 숫자 인덱스

<?php
$string = 'string';
echo "The character at index -2 is $string[-2].", PHP_EOL;
$string[-3] = 'o';
echo "Changing the character at index -3 to o gives $string.", PHP_EOL;
?>

위 예제는 다음을 출력해요:

The character at index -2 is n.
Changing the character at index -3 to o gives strong.

더 복잡한 내용에는 고급 구문을 사용해야 해요.

고급(중괄호) 구문

고급 구문은 임의의 접근자로 변수를 보간할 수 있게 해줘요.

string 표현을 가진 어떤 스칼라 변수, 배열 요소, 객체 속성(정적이든 아니든)이든 이 구문으로 포함할 수 있어요. 표현식은 string 밖에서 쓰는 것과 똑같이 쓰고 {}로 감싸면 돼요. {는 이스케이프할 수 없기 때문에, ${ 바로 뒤에 올 때만 이 구문으로 인식돼요. 리터럴 {$를 얻으려면 {\$를 써요. 몇 가지 예시로 명확히 해볼게요:

예제 #19 중괄호 구문

<?php
const DATA_KEY = 'const-key';
$great = 'fantastic';
$arr = [
    '1',
    '2',
    '3',
    [41, 42, 43],
    'key' => 'Indexed value',
    'const-key' => 'Key with minus sign',
    'foo' => ['foo1', 'foo2', 'foo3']
];

// Won't work, outputs: This is { fantastic}
echo "This is { $great}" . PHP_EOL;

// Works, outputs: This is fantastic
echo "This is {$great}" . PHP_EOL;

// To show curly braces in the output:
echo "This is {{$great}}" . PHP_EOL;

class Square {
    public $width;

    public function __construct(int $width) { $this->width = $width; }
}

$square = new Square(5);

// Works
echo "This square is {$square->width}00 centimeters wide." . PHP_EOL;


// Works, quoted keys only work using the curly brace syntax
echo "This works: {$arr['key']}" . PHP_EOL;


// Works
echo "This works: {$arr[3][2]}" . PHP_EOL;

echo "This works: {$arr[DATA_KEY]}" . PHP_EOL;

// When using multidimensional arrays, always use braces around arrays
// when inside of strings
echo "This works: {$arr['foo'][2]}" . PHP_EOL;

echo "This works: {$obj->values[3]->name}" . PHP_EOL;

echo "This works: {$obj->$staticProp}" . PHP_EOL;

// Won't work, outputs: C:\directory\{fantastic}.txt
echo "C:\directory\{$great}.txt" . PHP_EOL;

// Works, outputs: C:\directory\fantastic.txt
echo "C:\\directory\\{$great}.txt" . PHP_EOL;
?>

참고: 이 구문은 임의의 표현식을 허용하므로, 고급 구문 안에서 변수 변수를 사용할 수 있어요.

문자별 문자열 접근과 수정

string 안의 문자는 string 뒤에 대괄호 array 괄호로 원하는 문자의 0 기반 오프셋을 지정해서 접근하고 수정할 수 있어요(예: $str[42]). 이 목적을 위해 string을 문자들의 array라고 생각하면 돼요. 1자 이상을 추출하거나 교체할 때는 substr()substr_replace() 함수를 쓸 수 있어요.

참고: PHP 7.1.0부터 음수 문자열 오프셋도 지원해요. 이 오프셋은 문자열 끝에서부터의 위치를 나타내요. 이전에는 음수 오프셋이 읽을 때 E_NOTICE(빈 문자열을 반환)를, 쓸 때 E_WARNING(문자열을 그대로 둠)을 발생시켰어요.

참고: PHP 8.0.0 이전에는 같은 목적으로 중괄호를 써서 $str{42}처럼 접근할 수도 있었어요. 이 중괄호 구문은 PHP 7.4.0에서 권장하지 않게 되었고, PHP 8.0.0부터 더 이상 지원하지 않아요.

범위를 벗어난 오프셋에 쓰면 문자열이 공백으로 채워져요. 정수가 아닌 타입은 정수로 변환돼요. 잘못된 오프셋 타입은 **E_WARNING**을 발생시켜요. 할당된 문자열의 첫 문자만 사용돼요. PHP 7.1.0부터 빈 문자열을 할당하면 치명적 오류가 발생해요. 이전에는 NULL 바이트를 할당했어요.

내부적으로 PHP 문자열은 바이트 배열이에요. 그 결과 배열 대괄호로 문자열에 접근하거나 수정하는 것은 멀티바이트에 안전하지 않으므로, ISO-8859-1 같은 싱글바이트 인코딩의 문자열에서만 해야 해요.

참고: PHP 7.1.0부터 빈 문자열에 빈 인덱스 연산자를 적용하면 치명적 오류가 발생해요. 이전에는 빈 문자열이 조용히 배열로 변환됐어요.

예제 #20 몇 가지 문자열 예제

<?php
// Get the first character of a string
$str = 'This is a test.';
$first = $str[0];
var_dump($first);

// Get the third character of a string
$third = $str[2];
var_dump($third);

// Get the last character of a string.
$str = 'This is still a test.';
$last = $str[strlen($str)-1];
var_dump($last);

// Modify the last character of a string
$str = 'Look at the sea';
$str[strlen($str)-1] = 'e';
var_dump($str);
?>

문자열 오프셋은 정수이거나 정수처럼 생긴 문자열이어야 해요. PHP 8.0.0부터 그 외의 문자열은 TypeError를 던져요. 단, 정수로 시작하고 다른 문자가 뒤따르는 문자열은 **E_WARNING**을 발생시키고 그 앞의 정수로 해석돼요. PHP 8.0.0 이전에는 TypeError를 던지지 않고, 잘못된 오프셋이 **E_WARNING**을 대신 발생시켰어요.

예제 #21 잘못된 문자열 오프셋 예제

<?php
$str = 'abc';

$keys = [ '1', '1.0', 'x', '1x' ];

foreach ($keys as $keyToTry) {
    var_dump(isset($str[$keyToTry]));

    try {
        var_dump($str[$keyToTry]);
    } catch (TypeError $e) {
        echo $e->getMessage(), PHP_EOL;
    }

    echo PHP_EOL;
}
?>

PHP 8에서의 위 예제 출력:

bool(true)
string(1) "b"

bool(false)
Cannot access offset of type string on string

bool(false)
Cannot access offset of type string on string

bool(false)

Warning: Illegal string offset "1x" in example.php on line 10
string(1) "b"

PHP 7에서의 위 예제 출력:

bool(true)
string(1) "b"

bool(false)

Warning: Illegal string offset '1.0' in example.php on line 10
string(1) "b"

bool(false)

Warning: Illegal string offset 'x' in example.php on line 10
string(1) "a"

bool(false)

Notice: A non well formed numeric value encountered in example.php on line 10
string(1) "b"

참고: 다른 타입의 변수(적절한 인터페이스를 구현한 배열이나 객체는 제외)에 []{}로 접근하면 조용히 **null**을 반환해요.

참고: 문자열 리터럴 안의 문자는 []{}로 접근할 수 있어요.

참고: 문자열 리터럴 안의 문자에 {} 구문으로 접근하는 것은 PHP 7.4에서 권장하지 않게 되었어요. PHP 8.0에서 제거됐어요.

유용한 함수와 연산자

String은 '.'(점) 연산자로 연결할 수 있어요. '+' (더하기) 연산자는 이 용도로 동작하지 않는다는 점에 주의하세요. 자세한 내용은 String 연산자에서 다뤄요.

string 조작에 유용한 함수가 여러 가지 있어요.

일반 함수는 문자열 함수 섹션에서, 고급 찾기·교체 기능은 Perl 호환 정규 표현식 함수에서 확인할 수 있어요.

URL 문자열을 위한 함수와 문자열을 암호화/복호화하는 함수(Sodium과 Hash)도 있어요.

마지막으로 문자 타입 함수도 함께 봐요.

문자열로 변환 (Converting to string)

값은 (string) 캐스트나 strval() 함수로 string으로 변환할 수 있어요. String 변환은 string이 필요한 표현식 범위에서 자동으로 일어나요. echoprint 함수를 사용할 때, 또는 변수를 string과 비교할 때 그런 일이 벌어져요. Types와 Type Juggling 섹션이 다음 내용을 더 명확하게 해줄 거예요. settype() 함수도 함께 봐요.

bool true 값은 string "1"로 변환돼요. bool **false**는 ""(빈 문자열)로 변환돼요. 덕분에 boolstring 값을 서로 변환할 수 있어요.

intfloat는 숫자를 그대로 나타내는 string(float의 지수 부분 포함)으로 변환돼요. 부동소수점 숫자는 지수 표기(4.1E+6)로 변환될 수 있어요.

참고: PHP 8.0.0부터 소수점 문자는 항상 마침표(".")예요. PHP 8.0.0 이전에는 소수점 문자가 스크립트의 로캘(LC_NUMERIC 범주)에 정의됐어요. setlocale() 함수를 참고하세요.

Array는 항상 string "Array"로 변환돼요. 그래서 echoprint만으로는 array의 내용을 보여줄 수 없어요. 단일 요소를 보려면 echo $arr['foo'] 같은 구문을 써요. 전체 내용을 보는 팁은 아래를 참고해요.

objectstring으로 변환하려면 __toString 매직 메서드를 사용해야 해요.

Resource는 항상 "Resource id #1" 구조의 string으로 변환돼요. 여기서 1은 런타임에 PHP가 resource에 할당한 리소스 번호예요. 이 문자열의 정확한 구조에 의존해서는 안 되고 변경될 수 있지만, 스크립트가 실행되는 수명(웹 요청이나 CLI 프로세스) 동안 주어진 리소스에는 항상 유일하며 재사용되지 않아요. resource의 타입을 얻으려면 get_resource_type() 함수를 써요.

**null**은 항상 빈 문자열로 변환돼요.

위에서 말했듯이 array, object, resource를 직접 string으로 변환하면 타입 외에는 값에 대한 유용한 정보를 주지 못해요. 이 타입들의 내용을 더 효과적으로 검사하려면 print_r()var_dump() 함수를 봐요.

대부분의 PHP 값은 영구 저장을 위해 string으로 변환될 수도 있어요. 이 방법을 직렬화(serialization)라고 하고, serialize() 함수가 수행해요.

문자열 타입의 세부 사항 (Details of the String Type)

PHP의 string은 바이트 배열과 버퍼 길이를 나타내는 정수로 구현돼요. 이 바이트들이 어떤 문자로 번역되는지에 대한 정보는 없고, 그 작업은 프로그래머의 몫이에요. 문자열이 구성할 수 있는 값에는 제한이 없어요. 특히 값이 0인 바이트("NUL 바이트")는 문자열 어디에나 허용돼요. 다만 이 매뉴얼에서 "바이너리 안전(binary safe)"하지 않다고 말한 일부 함수는 문자열을 NUL 바이트 이후 데이터를 무시하는 라이브러리에 넘길 수 있어요.

이런 문자열 타입의 성격이 PHP에 별도의 "byte" 타입이 없는 이유를 설명해요. 문자열이 그 역할을 맡아요. 텍스트 데이터가 아닌 것을 반환하는 함수(예: 네트워크 소켓에서 읽은 임의 데이터)도 여전히 문자열을 반환해요.

PHP가 문자열에 특정 인코딩을 강요하지 않기 때문에, 문자열 리터럴이 어떻게 인코딩되는지 궁금할 수 있어요. 예를 들어 "á"라는 문자열은 "\xE1"(ISO-8859-1)과 같을까요, "\xC3\xA1"(UTF-8, C form)과 같을까요, "\x61\xCC\x81"(UTF-8, D form)과 같을까요, 아니면 다른 어떤 표현일까요? 답은 문자열이 스크립트 파일에 인코딩된 그대로 인코딩된다는 것이에요. 즉 스크립트가 ISO-8859-1로 쓰였다면 문자열도 ISO-8859-1로 인코딩되는 식이에요. 다만 Zend Multibyte가 활성화된 경우에는 이 규칙이 적용되지 않아요. 그 경우 스크립트가 임의의 인코딩(명시적으로 선언되거나 감지되는)으로 쓰일 수 있고, 일정한 내부 인코딩으로 변환된 후 그 인코딩이 문자열 리터럴에 사용돼요. 스크립트의 인코딩(또는 Zend Multibyte가 활성화된 경우 내부 인코딩)에는 몇 가지 제약이 있는데, 거의 항상 그 인코딩이 ASCII의 호환 수퍼셋(예: UTF-8이나 ISO-8859-1)이어야 한다는 뜻이에요. 다만 같은 바이트 값이 초기/비초기 시프트 상태에서 쓰일 수 있는 상태 의존 인코딩은 문제가 될 수 있어요.

물론 유용하려면 텍스트를 다루는 함수가 문자열이 어떻게 인코딩되는지에 대한 어떤 가정을 해야 해요. 안타깝게도 PHP의 함수 전반에 걸쳐 이 부분은 변이가 많아요:

  • 일부 함수는 문자열이 어떤 (아무) 싱글바이트 인코딩으로 인코딩됐다고 가정하지만, 그 바이트들을 특정 문자로 해석할 필요는 없어요. 예를 들어 substr(), strpos(), strlen(), strcmp()가 그런 경우예요. 이런 함수들이 메모리 버퍼에 대해 동작한다고 생각해도 돼요. 즉 바이트와 바이트 오프셋으로 동작해요.
  • 다른 함수는 문자열의 인코딩을 매개변수로 받거나 제공되지 않으면 기본값을 가정해요. htmlentities()와 mbstring 확장의 대부분 함수가 그런 경우예요.
  • 또 다른 함수는 현재 로캘을 사용하지만(setlocale() 참고) 바이트 단위로 동작해요.
  • 마지막으로 특정 인코딩, 보통 UTF-8을 사용한다고 가정할 수도 있어요. intl 확장의 대부분 함수와 PCRE 확장(마지막 경우, u 수정자를 사용할 때만)이 그런 경우예요.

궁극적으로 유니코드를 사용하는 올바른 프로그램을 작성하려면 동작하지 않아 데이터를 손상시킬 가능성이 높은 함수를 조심스럽게 피하고, 일반적으로 intl과 mbstring 확장에서 동작을 잘하는 함수를 대신 사용하는 일에 달려 있어요. 다만 유니코드 인코딩을 처리할 수 있는 함수를 사용하는 것은 시작에 불과해요. 언어가 제공하는 함수와 무관하게 유니코드 사양을 아는 것이 필수적이에요. 예를 들어 대문자와 소문자만 있다고 가정하는 프로그램은 잘못된 가정을 하고 있는 거예요.