PHP에서 형 변환 변수를 사용하는 실용적인 이유는 무엇입니까?


45

우리 대부분이 알고 있듯이 PHP는 타이핑 합니다. 그렇지 않은 사람들을 위해 PHP.net은 말합니다.

PHP는 변수 선언에서 명시 적 유형 정의를 요구하지 않거나 지원하지 않습니다. 변수 유형은 변수가 사용되는 컨텍스트에 따라 결정됩니다.

그것을 좋아하거나 미워합니다. PHP는 변수를 즉석에서 다시 캐스팅합니다. 따라서 다음 코드가 유효합니다.

$var = "10";
$value = 10 + $var;
var_dump($value); // int(20)

PHP를 사용하면 다음과 같이 변수를 명시 적으로 캐스팅 할 수 있습니다.

$var = "10";
$value = 10 + $var;
$value = (string)$value;
var_dump($value); // string(2) "20"

그것은 모두 시원합니다 ...하지만, 내 삶을 위해, 나는 이것을하는 실질적인 이유를 생각할 수 없습니다.

Java와 같이 지원 언어로 강력한 타이핑에 문제가 없습니다. 괜찮습니다. 완전히 이해합니다. 또한 함수 매개 변수에서 유형 힌트 의 유용성을 알고 있으며 완전히 이해하고 있습니다.

타입 캐스팅과 관련된 문제는 위의 인용문으로 설명됩니다. PHP가 원하는대로 타입 바꿀 수 있다면 , 타입 을 강제로 캐스팅 한 후에도 그렇게 할 수 있습니다 . 그리고 그렇게 할 수 에 - 더 - 플라이 당신이 작업의 특정 유형을 필요로 할 때. 그러면 다음이 유효합니다.

$var = "10";
$value = (int)$var;
$value = $value . ' TaDa!';
var_dump($value); // string(8) "10 TaDa!"

그래서 요점이 뭐야?


PHP 에서 사용자 정의 유형 캐스팅 이 적합한 세계의 이론적 예를 보자 .

  1. 캐스트 변수 $fooint→ 로 강제 실행 (int)$foo합니다.
  2. 변수에 문자열 값을 저장하려고합니다 $foo.
  3. PHP는 예외를 던졌습니다 !! ← 말이 되겠네요. 갑자기 사용자 정의 유형 캐스팅의 이유가 존재합니다!

PHP가 필요에 따라 상황을 전환한다는 사실은 사용자 정의 유형 캐스팅의 요지를 모호하게 만듭니다. 예를 들어 다음 두 코드 샘플은 동일합니다.

// example 1
$foo = 0;
$foo = (string)$foo;
$foo = '# of Reasons for the programmer to type cast $foo as a string: ' . $foo;

// example 2
$foo = 0;
$foo = (int)$foo;
$foo = '# of Reasons for the programmer to type cast $foo as a string: ' . $foo;

원래이 질문을 한 지 1 년 후, 실제 환경에서 누가 타입 캐스팅을 사용하고 있는가? 당신은 정말입니다.

식당 메뉴를 위해 웹 사이트에 돈 가치를 표시해야했습니다. 사이트를 디자인하려면 후행 0을 다듬어야하므로 디스플레이가 다음과 같이 보입니다.

Menu Item 1 .............. $ 4
Menu Item 2 .............. $ 7.5
Menu Item 3 .............. $ 3

내가 찾은 가장 좋은 방법은 변수를 float로 캐스팅하는 것이 아닙니다.

$price = '7.50'; // a string from the database layer.
echo 'Menu Item 2 .............. $ ' . (float)$price;

PHP는 float의 후행 0을 자른 다음 float를 문자열로 다시 연결하여 연결합니다.


이것은-> $ value = $ value입니다. '타다!'; $ value의 최종 값에 할당하기 전에 $ value를 문자열로 캐스트합니다. 타입 캐스트를 강요하면 타입 캐스트를 얻는다는 것은 놀라운 일이 아닙니다. 요점이 무엇인지 묻는 점이 무엇인지 확실하지 않습니까?
Chris

"# 3. PHP가 예외를 던졌습니다 !! <--- 그건 말이됩니다." 실제로 그것은 전혀 의미가 없습니다. Java, JavaScript 또는 내가 알고있는 다른 C 구문 언어에서도 문제가되지 않습니다. 올바른 마음으로 누가 바람직한 행동으로 보겠습니까? 어디에서나(string) 캐스트 를 원하십니까 ?
Nicole

@Renesis : 당신은 나를 오해합니다. 내 말은 사용자가 변수를 타입 캐스팅 한 경우에만 예외가 발생한다는 것입니다. 정상적인 동작 (PHP가 캐스팅을 수행하는 곳)은 물론 예외를 발생시키지 않습니다. 사용자 정의 형식 캐스팅이 moot 이라고 말하려고 하지만 예외가 발생하면 갑자기 의미가 있습니다.
Stephen

당신이 $intval.'bar'예외를 던졌다 고 말하는 경우 에도, 나는 여전히 동의하지 않습니다. 그것은 어떤 언어에서도 예외를 던지지 않습니다. (내가 아는 모든 언어는 자동 전송 또는을 수행합니다 .toString()). 당신이 $intval = $stringval예외를 던졌다 고 말하는 경우, 당신은 강력한 유형의 언어에 대해 이야기하고 있습니다. 무례하게 들릴 생각은 아니 었으므로 미안합니다. 나는 그것이 모든 개발자가 익숙한 것에 반하고 훨씬 덜 편리하다고 생각합니다.
Nicole

@Stephen-조사 후 답변을 올렸습니다. 정말 흥미로운 결과-두 가지 경우가 캐스팅의 목적을 보여줄 것이라고 생각했지만 PHP는 생각보다 훨씬 이상합니다.
Nicole

답변:


32

약한 유형의 언어에서 유형 캐스팅은 유형 연산에서 모호성을 제거하기 위해 존재합니다. 그렇지 않으면 컴파일러 / 인터프리터가 순서 또는 다른 규칙을 사용하여 사용할 연산을 가정합니다.

일반적으로 PHP는이 패턴을 따르지만, 내가 확인한 사례 중 PHP는 반 직관적으로 행동했습니다.

JavaScript를 비교 언어로 사용하는 경우가 있습니다.

문자열 연결

별도의 문자열 연결 ( .)과 더하기 ( +) 연산자 가 있기 때문에 PHP에서는 문제가되지 않습니다 .

자바 스크립트
var a = 5;
var b = "10"
var incorrect = a + b; // "510"
var correct = a + Number(b); // 15

문자열 비교

컴퓨터 시스템에서 종종 "5"는 숫자로 해석되지 않으므로 "10"보다 큽니다. PHP에서는 그렇지 않습니다. 문자열 모두 문자열이지만 숫자임을 인식하고 캐스트가 필요하지 않습니다.

자바 스크립트
console.log("5" > "10" ? "true" : "false"); // true
PHP
echo "5" > "10" ? "true" : "false";  // false!

함수 서명 입력

PHP는 함수 시그니처에 대한 기본 형식 검사를 구현하지만 불행히도 사용하기가 거의 불가능합니다.

내가 잘못하고 있다고 생각했지만 문서에 대한 의견은 배열 이외의 내장 유형을 PHP 함수 서명에 사용할 수 없음을 확인합니다-오류 메시지가 잘못되었습니다.

PHP
function testprint(string $a) {
    echo $a;
}

$test = 5;
testprint((string)5); // "Catchable fatal error: Argument 1 passed to testprint()
                      //  must be an instance of string, string given" WTF?

그리고 내가 알고있는 다른 언어와 달리 이해하는 유형을 사용하더라도 null을 더 이상 해당 인수 ( must be an instance of array, null given)에 전달할 수 없습니다 . 어리 석다

부울 해석

[ 편집 ] : 이것은 새로운 것입니다. 다른 경우를 생각하고 다시 논리가 JavaScript와 반대입니다.

자바 스크립트
console.log("0" ? "true" : "false"); // True, as expected. Non-empty string.
PHP
echo "0" ? "true" : "false"; // False! This one probably causes a lot of bugs.

결론적으로, 내가 생각할 수있는 유일한 유용한 사례는 ... (드럼 롤)

유형 잘림

즉, 한 유형의 값 (예 : 문자열)이 있고 다른 유형 (int)으로 해석하려고하고 해당 유형의 유효한 값 세트 중 하나가되도록하려면 다음을 수행하십시오.

$val = "test";
$val2 = "10";
$intval = (int)$val; // 0
$intval2 = (int)$val2; // 10
$boolval = (bool)$intval // false
$boolval2 = (bool)$intval2 // true
$props = (array)$myobject // associative array of $myobject's properties

나는 더 많은 가치를 포괄하는 유형으로 어떤 업 캐스트가 실제로 당신을 얻을 수 있는지 알 수 없습니다.

따라서 제안 된 타이핑 사용에 동의하지 않지만 (기본적으로 정적 타이핑을 제안 하고 있지만 유형 으로 강제 캐스팅 된 경우에만 오류가 발생하여 혼란을 초래할 수있는 모호함) 문제는 분명히 캐스팅 때문에이있다 매우 PHP에서 약간의 목적을.


좋아, E_NOTICE그럼 어때? :)
Stephen

@Stephen E_NOTICE은 괜찮을 수도 있지만 모호한 상태가 관련이 있습니다. 변수가 해당 상태 (다른 곳에서 캐스팅 된 경우)에있는 경우 1 비트 코드를 보면 어떻게 알 수 있습니까? 또한 다른 조건을 찾아 내 답변에 추가했습니다.
Nicole

1
부울 평가의 경우, PHP 문서는 부울로 평가할 때 거짓으로 간주되는 것을 명확하게 나타내며 빈 문자열과 문자열 "0"은 모두 거짓으로 간주됩니다. 따라서 이것이 비만인 것처럼 느껴질지라도 그것은 정상적이고 예상되는 행동입니다.
Jacek Prucia

: 혼란에 비트를 추가 echo "010" == 010 하고 echo "0x10" == 0x10;-)
vartec

1
참고로하는 것이 PHP 7 , 스칼라 타입 힌트에이 답변의 노트는 정확하지 않습니다.
John V.

15

약한 / 강한 / 동적 / 정적 유형 개념을 혼합합니다.

PHP는 약하고 역동적이지만 문제는 동적 유형 개념에 있습니다. 즉, 변수에는 유형이 없으며 값에는 없습니다.

'유형 캐스팅'은 원본과 다른 유형의 새로운 값을 생성하는 표현식입니다. 변수에 영향을 미치지 않습니다 (관련된 경우).

캐스트 값을 정기적으로 입력하는 상황은 숫자 SQL 매개 변수입니다. SQL 문에 삽입하는 입력 값을 삭제 / 이스케이프 처리하거나 매개 변수화 된 쿼리를 사용하는 것이 좋습니다. 그러나 정수가되어야하는 값을 원한다면 캐스팅하는 것이 훨씬 쉽습니다.

치다:

function get_by_id ($id) {
   $id = (int)$id;
   $q = "SELECT * FROM table WHERE id=$id LIMIT 1";
   ........
}

첫 줄을 생략하면 $idSQL 주입을위한 쉬운 벡터가 될 것입니다. 캐스트는 그것이 무해한 정수인지 확인합니다. SQL을 삽입하려고하면id=0


나는 그것을 받아 들일 것이다. 이제 타입 캐스팅의 유용성까지?
Stephen

SQL 삽입을 가져 오는 것이 재미있다. 나는이 기술을 사용하여 사용자 입력을 소독하는 누군가와 SO에 대해 논쟁하고있었습니다. 그러나이 방법으로 mysql_real_escape_string($id);아직 해결 되지 않은 문제는 무엇 입니까?
Stephen

물론 더 짧습니다 :-), 문자열의 경우 매개 변수가있는 쿼리를 사용하거나 (이전 mysql 확장을 사용하는 경우) 이스케이프 처리하십시오.
Javier

2
mysql_real_escape_string()'0x01ABCDEF'와 같은 문자열 (예 : 정수의 16 진 표현)에 대해서는 아무 작업도하지 않습니다. 유니 코드 lucklily가 아닌 일부 멀티 바이트 인코딩에서는 이와 같은 문자열을 사용하여 쿼리를 중단 할 수 있습니다 (MySQL에서 따옴표가 포함 된 것으로 평가되기 때문에). 이것이 정수 값을 다루기위한 최선의 선택이 아닌 것도 mysql_real_escape_string()아닙니다 is_int(). 타입 캐스팅입니다.
Mchl

더 자세한 내용의 링크 : ilia.ws/archives/…
Mchl

4

내가 찾은 PHP에서 유형 캐스팅을 사용하는 한 가지 방법 :

서버에서 PHP 스크립트에 http 요청하여 데이터베이스에서 데이터를 검색하는 Android 앱을 개발 중입니다. 이 스크립트는 PHP 객체 (또는 연관 배열) 형태로 데이터를 저장하고 JSON 객체로 앱에 반환됩니다. 유형 캐스팅이 없으면 다음과 같은 내용이 표시됩니다.

{ "user" : { "id" : "1", "name" : "Bob" } }

그러나 (int)PHP 객체를 저장할 때 사용자 ID에 PHP 유형 캐스팅 을 사용하면 대신 앱으로 반환됩니다.

{ "user" : { "id" : 1, "name" : "Bob" } }

그런 다음 앱에서 JSON 객체를 구문 분석하면 ID를 정수로 구문 분석하지 않아도됩니다!

매우 유용합니다.


외부의 강력한 형식의 시스템에서 사용할 데이터 형식을 고려하지 않았습니다. +1
Stephen

이는 Elasticsearch와 같은 외부 시스템과 JSON을 대화 할 때 특히 그렇습니다. 로 json_encode () - 에드 값은 "5"값 5에 비해 매우 다른 결과를 줄 것이다
요한 프레드릭 바렌

3

하나의 예는 __toString 방법으로 객체이다 $str = $obj->__toString();$str = (string) $obj;. 두 번째는 타이핑이 훨씬 적으며, 구두점은 입력 시간이 오래 걸립니다. 또한 다른 사람들이 동의하지 않을 수도 있지만 더 읽기 쉽다고 생각합니다.

또 다른 하나의 요소의 배열을하고있다 : array($item);(array) $item;. 이렇게하면 스칼라 유형 (정수, 자원 등)이 배열 안에 배치됩니다.
선택적 $item으로 객체 인 경우 속성이 해당 값의 키가됩니다. 그러나 객체-> 배열 변환은 약간 이상하다고 생각합니다. 개인 속성과 보호 속성은 배열의 일부이며 이름이 바뀝니다. PHP 문서 를 인용하려면 : 개인 변수는 클래스 이름 앞에 변수 이름이 붙습니다. 보호 된 변수는 변수 이름 앞에 '*'가 붙습니다.

또 다른 용도는 GET / POST 데이터를 데이터베이스에 적합한 유형으로 변환하는 것입니다. MySQL 은이 자체를 처리 할 수 ​​있지만 더 ANSI 호환 서버는 데이터를 거부 할 수 있다고 생각합니다. 내가 데이터베이스에 대해서만 언급하는 이유는 대부분의 다른 경우 데이터가 특정 시점에 유형에 따라 작업을 수행하기 때문입니다 (즉, int / float는 일반적으로 데이터베이스에서 계산을 수행하는 등).


타입 캐스팅이 어떻게 작동하는지에 대한 훌륭한 예입니다. 그러나 나는 그들이 필요 를 채우고 있다고 확신하지 못한다 . 예, 객체를 배열로 변환 할 수 있지만 그 이유는 무엇입니까? 그런 다음 새 배열에서 무수한 PHP 배열 함수를 사용할 수 있기 때문에 유용한 방법을 알 수는 없습니다. 또한 PHP는 일반적으로 문자열 쿼리를 생성하여 MySQL 데이터베이스로 전송하므로 변수 유형은 관련이 없습니다 ( 쿼리를 빌드 할 때 int또는 문자열을 자동으로 변환 float할 때 발생 함). (array) $item이다 깔끔한 하지만, 유용?
Stephen

나는 실제로 동의합니다. 내가 타이핑하는 동안, 나는 몇 가지 용도를 생각할 것이라고 생각했지만 그렇지 않았습니다. 데이터베이스의 경우 매개 변수가 쿼리 문자열의 일부이면 캐스팅이 목적이 아닙니다. 그러나 매개 변수가있는 쿼리를 사용하는 경우 (항상 좋은 방법 임) 매개 변수 유형을 지정할 수 있습니다.
Alan Pearce

아하! 매개 변수화 된 쿼리에서 유효한 이유를 발견했을 수 있습니다.
Stephen

0

이 스크립트는 :

$tags = _GET['tags'];
foreach ($tags as $tag) {
    echo 'tag: ', $tag;
}

첫 번째 경우에는 배열을 반환하지만 두 번째는 아닌 배열을 반환 하기 때문에 script.php?tags[]=one제대로 실행 되지만 실패합니다 . 스크립트는 배열을 예상하도록 작성되었으므로 (스크립트로 전송되는 쿼리 문자열에 대한 제어력이 적음) 다음에서 결과를 적절히 캐스팅하여 문제를 해결할 수 있습니다 .script.php?tags=one_GET['tags']_GET

$tags = (array) _GET['tags'];
foreach ($tags as $tag) {
    echo 'tag: ', $tag;
}

0

또한 신뢰할 수없는 데이터가 문제를 일으키지 않도록하기 위해 빠르고 더러운 방법으로 사용될 수도 있습니다.

$amount = (float) $_POST['amount'];

if( $amount > 0 ){
    $remoteService->doacalculationwithanumber( $amount );    
}

분명히 이것은 if 문에서 비교 연산자에 의해 결함이 있고 암시 적으로 처리되지만 코드가 무엇을하는지 정확히 알 수 있도록 도와줍니다.


1
그것이 깨지지 않는 것을 제외하고. $_POST['amount']가비지 문자열을 포함 하더라도 PHP는 0보다 크지 않은 것으로 평가합니다. 양수를 나타내는 문자열이 포함 된 경우 true로 평가됩니다.
Stephen

1
완전히 사실이 아닙니다. 조건부 내에서 $ amount가 제 3 자 서비스로 전달되어 숫자를 받아야합니다. 누군가가 $ _POST [ 'amount'] = "100 bobbins"를 전달한 경우 (float)를 제거하면 여전히 조건부 통과가 허용되지만 $ amount는 숫자가 아닙니다.
Gruffputs

-2

자주 사용하는 PHP 리 캐스팅 변수의 "사용"중 하나는 외부 소스 (사용자 입력 또는 데이터베이스)에서 데이터를 검색 할 때입니다. 이를 통해 코더 (개발자에게 말하지 않았 음 )는 다른 소스에서 사용 가능한 여러 데이터 유형을 무시 (또는 배우지 못함) 할 수 있습니다.

내가 상속하고 여전히 유지하는 코드를 가진 한 코더 (개발자 에게 말하지 않았다는 점에 유의) 슈퍼 변수에 반환 된 문자열 "20"$_GET20 + 20 추가 할 때 정수 연산 사이에 차이있음 을 알지 못하는 것 같습니다 데이터베이스의 값 그녀는 PHP가 .문자열 연결에 사용 +하고 다른 모든 언어 와 달리 사용하는 것이 운이 좋다 . 왜냐하면 그녀의 코드가 두 개의 문자열 ( varcahrMySQL과 a 값 $_GET)을 추가하고 int를 얻었 기 때문이다.

이것이 실제적인 예입니까? 코더가 작업하는 데이터 유형을 모른 채로 벗어날 수 있다는 의미에서만. 나는 개인적으로 그것을 싫어한다.


2
이 답변이 토론에 가치를 더하는 방법을 모르겠습니다. PHP가 엔지니어 (또는 프로그래머 또는 코더 등)가 문자열에 대해 수학 연산을 수행 할 수 있다는 사실은 이미 많은 질문에서 명확합니다.
Stephen

스티븐 감사합니다. "PHP는 데이터 유형이 무엇인지 모르는 사람들이 이상적인 조건에서 기대하는 것을 수행하는 응용 프로그램을 만들 수있게합니다"라고 말하기 위해 너무 많은 단어를 사용했을 것입니다.
dotancohen
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.