PHP
유형 저글링 및 비 엄격 비교 문제
수색…
타입 저글이란 무엇입니까?
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");