web-dev-qa-db-ja.com

PHPで変数を型キャストする、これを行う実際的な理由は何ですか?

ほとんどの人が知っているように、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のように、それをサポートする言語での強い型付けには問題はありません。それは結構です、そして私はそれを完全に理解しています。また、私は-を認識しており、関数パラメーターで- type hinting の有用性を完全に理解しています。

型キャストで私が抱えている問題は、上記の引用で説明されています。 PHPがタイプを交換できる場合at-willであれば、それはあなたの後でも可能です強​​制キャスト型;そして特定の型が必要なときにオンザフライこれにより、次のことが有効になります。

$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が必要に応じて状況を切り替えるという事実により、ユーザー定義型キャストのポイントは曖昧になります。たとえば、次の2つのコードサンプルは同等です。

// 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年後、実用的な環境で型キャストを使用しているのは誰でしょうか。敬具。

要件は、レストランのメニューのウェブサイトに金額を表示することでした。サイトのデザインでは、末尾のゼロを削除する必要があるため、表示は次のようになります。

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

変数をフロートとしてキャストするのが無駄だったことがわかった最良の方法:

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

PHPは、フロートの末尾のゼロを削除してから、フロートを連結用の文字列として再キャストします。

45
Stephen

弱く型付けされた言語では、型キャストは型付けされた操作のあいまいさを取り除くために存在します。そうでない場合、コンパイラー/インタープリターは順序またはその他の規則を使用して、使用する操作を想定します。

通常、私はPHPがこのパターンに従っていると言いますが、私がチェックしたケースの中で、PHPはそれぞれで直感的に振る舞いませんでした。

以下は、比較言語としてJavaScriptを使用した場合の例です。

文字列連結

PHPでは個別のstring concatenation.)と加算(+)演算子。

JavaScript
var a = 5;
var b = "10"
var incorrect = a + b; // "510"
var correct = a + Number(b); // 15

文字列の比較

コンピュータシステムでは、「5」は数値として解釈されないため、「10」より大きいことがよくあります。 PHPではそうではなく、bothが文字列であっても、それらが数値であることを認識し、キャストの必要性を排除します):

JavaScript
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)。なんてばかな。

ブール解釈

[Edit]:これは新しいものです。私は別のケースを考えました、そしてまたロジックはJavaScriptから逆転されます。

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)として解釈し、そのタイプの値の有効なセットの1つになるように強制したい場合:

$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

(より多くの値を含む型への)アップキャストが本当にあなたに何をもたらすかわかりません。

したがって、私はあなたがタイピングの提案された使用に同意しませんが(あなたは本質的に 静的タイピング を提案していますが、それがforce-castタイプはエラーをスローします—これは混乱を招きます)、PHPではキャストがvery目的をほとんど持っていないので、それは良い質問だと思います。

32
Nicole

あなたは弱い/強いと動的/静的なタイプの概念を混ぜています。

PHPは脆弱で動的ですが、問題は動的型の概念にあります。つまり、変数には型がなく、値には型があります。

「型キャスト」は、元の型とは異なる型の新しい値を生成する式です。変数に対しては何も行いません(含まれている場合)。

キャスト値を定期的に入力する1つの状況は、数値SQLパラメーターです。 SQLステートメントに挿入する入力値をサニタイズ/エスケープするか、または(はるかに良い)パラメーター化されたクエリを使用する必要があります。ただし、整数でなければならない値が必要な場合は、キャストする方がはるかに簡単です。

考慮してください:

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

最初の行を省略した場合、$idはSQLインジェクションのための簡単なベクトルです。キャストはそれが無害な整数であることを確認します。 SQLを挿入しようとすると、単にid=0

15
Javier

私が見つけたPHPでの型キャストの1つの使用法:

私はAndroidアプリを開発しています。これは、データベースからデータを取得するためにサーバー上のhttp PHPスクリプトにHTTPリクエストを送信します。スクリプトは、 PHPオブジェクト(または連想配列)の形式のデータであり、アプリにJSONオブジェクトとして返されます。型キャストを行わないと、次のようになります。

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

しかし、PHP type Casting (int)ユーザーIDにPHPオブジェクトを保存すると、代わりにアプリに返されます。

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

次に、JSONオブジェクトがアプリで解析されると、IDを整数に解析する必要がなくなります。

ほら、とても便利。

4
Ryan

1つの例は、__ toStringメソッドを持つオブジェクトです:$str = $obj->__toString(); vs $str = (string) $obj;。 2番目の入力ははるかに少なく、余分なものは句読点であり、入力に時間がかかります。他の人が反対するかもしれませんが、私はそれもより読みやすいと思います。

もう1つは、単一要素の配列を作成することです:array($item); vs (array) $item;。これにより、配列内にスカラー型(整数、リソースなど)が配置されます。
代替として、$itemはオブジェクトであり、そのプロパティはそれらの値のキーになります。ただし、オブジェクトから配列への変換は少し奇妙だと思います。プライベートプロパティと保護プロパティは配列の一部であり、名前が変更されています。 PHPドキュメント を引用するには:プライベート変数は、変数名の前にクラス名が付加されています。保護された変数には、変数名の前に「*」が付いています。

もう1つの用途は、GET/POSTデータをデータベースに適したタイプに変換することです。 MySQLはこれ自体を処理できますが、ANSI準拠のサーバーが多いほどデータを拒否する可能性があります。私がデータベースについてのみ言及する理由は、他のほとんどの場合、データはある時点でそのタイプに従って操作が実行されるためです(つまり、int/floatsは通常、それらに対して実行される計算などを持ちます)。

3
Alan Pearce

また、信頼できないデータが何かを壊さないようにするための迅速でダーティな方法としても使用できます。

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

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

これは明らかに欠陥があり、ifステートメントの比較演算子によって暗黙的に処理されますが、コードが何をしているかを正確に把握するのに役立ちます。

0
Gruffputs

このスクリプト:

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

script.php?tags[]=oneは最初のケースでは配列を返すが2番目のケースでは配列を返さないため、script.php?tags=oneの場合は正常に実行されますが、_GET['tags']の場合は失敗します。スクリプトは配列を想定して記述されているため(そして、スクリプトに送信されるクエリ文字列を制御できません)、問題は_GETからの結果を適切にキャストすることで解決できます。

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