수색…


타입 저글이란 무엇입니까?

PHP는 느슨하게 입력 된 언어입니다. 즉, 기본적으로 표현식의 피연산자가 동일한 (또는 호환 가능한) 유형 일 필요는 없습니다. 예를 들어 문자열에 숫자를 추가하여 작동시킬 것으로 기대할 수 있습니다.

var_dump ("This is example number " . 1);

출력은 다음과 같습니다.

string (24) "이것은 예제 번호 1입니다"

PHP는 호환되지 않는 변수 유형을 요청한 작업을 수행 할 수있는 유형으로 자동 변환하여이를 수행합니다. 위의 경우 정수 리터럴 1을 문자열로 변환합니다. 즉, 문자열 리터럴에 연결할 수 있습니다. 이를 유형 저글링이라고합니다. 이것은 PHP의 매우 강력한 기능이지만, 모르는 사람이라면 머리카락을 많이 끌어 당길 수있는 기능이며 보안 문제로 이어질 수도 있습니다.

다음을 고려하세요:

if (1 == $variable) {
    // do something
}

그 의도는 프로그래머가 변수가 1이라는 값을 가지고 있는지 확인하는 것 같습니다. 그러나 $ variable에 "1과 1/2"의 값이 있으면 어떻게 될까요? 대답은 당신을 놀라게 할 수도 있습니다.

$variable = "1 and a half";
var_dump (1 == $variable);

결과는 다음과 같습니다.

bool (true)

왜 이런 일이 일어 났습니까? PHP는 문자열 "1과 1/2"이 정수가 아니라는 것을 깨달았 기 때문에 정수 1과 비교해야합니다. 실패하는 대신 PHP는 유형 저글링을 시작하고 변수를 정수. 정수로 변환하여 캐스팅 할 수있는 문자열의 시작 부분에있는 모든 문자를 가져 와서 수행합니다. 숫자로 처리 할 수없는 문자를 만나 자마자 멈 춥니 다. 따라서 "1 1/2"은 정수 1로 형 변환됩니다.

허락하신다면, 이것은 매우 고안된 예이지만 문제를 입증하는 데 도움이됩니다. 다음 몇 가지 예는 실제 소프트웨어에서 발생하는 저글링 유형으로 인해 발생하는 오류를 다루는 몇 가지 사례를 다룹니다.

파일에서 읽기

파일에서 읽을 때, 우리는 그 파일의 끝에 도달했을 때를 알 수 있기를 원합니다. fgets() 가 파일의 끝에서 false를 반환한다는 것을 알면, 이것을 루프의 조건으로 사용할 수 있습니다. 그러나 마지막 읽기에서 반환 된 데이터가 부울 false 로 평가되는 경우 파일 읽기 루프가 너무 일찍 종료 될 수 있습니다.

$handle = fopen ("/path/to/my/file", "r");

if ($handle === false) {
    throw new Exception ("Failed to open file for reading");
}

while ($data = fgets($handle)) {
    echo ("Current file line is $data\n");
}

fclose ($handle);

읽을 파일에 빈 줄이 포함되어 있으면 빈 문자열이 부울 false 로 평가되기 때문에 while 루프가 그 지점에서 종료됩니다.

대신 엄격한 등가 연산자를 사용하여 부울 false 값을 명시 적으로 확인할 수 있습니다.

while (($data = fgets($handle)) !== false) {
    echo ("Current file line is $data\n");
}

이는 고안된 예제입니다. 실생활에서는 다음과 같은 루프를 사용합니다.

while (!feof($handle)) {
    $data = fgets($handle);
    echo ("Current file line is $data\n");
}

또는 모든 것을 다음으로 대체하십시오.

$filedata = file("/path/to/my/file");
foreach ($filedata as $data) {
    echo ("Current file line is $data\n");
}

놀라운 놀라움

switch 문은 엄격하지 않은 비교를 사용하여 일치 항목을 결정합니다. 이것은 몇 가지 불쾌한 놀라움으로 이어질 수 있습니다. 예를 들어, 다음 문을 고려하십시오.

switch ($name) {
    case 'input 1':
        $mode = 'output_1';
        break;
    case 'input 2':
        $mode = 'output_2';
        break;
    default:
        $mode = 'unknown';
        break;
}

이것은 매우 단순한 명령문이며 $name 이 문자열 일 때 예상대로 작동하지만 그렇지 않은 경우 문제를 일으킬 수 있습니다. 예를 들어 $name 이 정수 0 이면 비교 중에 유형 저글링이 발생합니다. 그러나 switch 문에있는 조건이 아닌 case 명령문의 리터럴 값입니다. 문자열 "input 1" 정수로 변환된다 0 정수의 입력 값과 일치 0 . 이것은 정수 0 의 값을 제공하면 첫 번째 경우가 항상 실행됩니다.

이 문제에 대한 해결책은 다음과 같습니다.

명시 적 주조

값을 비교하기 전에 문자열로 타입 변환 할 수 있습니다.

switch ((string)$name) {
...
}

또는 문자열을 반환하는 함수를 사용할 수도 있습니다.

switch (strval($name)) {
...
}

이 두 가지 방법 모두 값이 case 문에있는 값과 동일한 유형인지 확인합니다.

switch 피하기

if 문을 사용하면 비교 방법을 제어 할 수 있으므로 엄격한 비교 연산자 를 사용할 수 있습니다 .

if ($name === "input 1") {
    $mode = "output_1";
} elseif ($name === "input 2") {
    $mode = "output_2";
} else {
    $mode = "unknown";
}

엄격한 타이핑

PHP 7.0 이후 엄격한 타이핑을 통해 타입 저글링의 유해한 영향을 줄일 수 있습니다. 이 포함시킴으로써 declare 파일의 첫 번째 행으로 문을, PHP는 매개 변수 유형 선언을 강제로 적용하고 던져 유형 선언을 반환 TypeError 예외.

declare(strict_types=1);

예를 들어, 다음 코드는 매개 변수 유형 정의를 사용하면 실행시 TypeError 유형의 catchable 예외를 throw합니다.

<?php
declare(strict_types=1);

function sum(int $a, int $b) {
    return $a + $b;
}

echo sum("1", 2);

마찬가지로이 코드는 반환 형식 선언을 사용합니다. 정수가 아닌 다른 값을 반환하려고 시도하면 예외가 발생합니다.

<?php
declare(strict_types=1);

function returner($a): int {
    return $a;
}

returner("this is a string");


Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow