こんにちは!
「鬼に金棒。ジジイにIT」という信念?!のもと、ビジネスからエンジニアリングの世界に飛び込んだtaketo45です。
当初JavaScriptを使ってプログラミング学習と開発をしていましたが、サーバーサイドの言語であるPHPの学習を始めた際に、様々な「えっ!?」という場面に遭遇しました。同じように JavaScript から PHP に挑戦する方の参考になればと思い、私が躓いたポイントをまとめてみました。
1. 変数の代入で躓く - ||
と ??
の違い
最初に大きく躓いたのが、デフォルト値の設定です。JavaScriptでは以下のように書いていました:
const value = someValue || "デフォルト値";
PHPで同じように書いたところ、想定と全く違う動きに...😱
$value = $someValue || "デフォルト値"; // true や false が入ってくる!?
正しくは以下のように書く必要があります
$value = $someValue ?? "デフォルト値";
それでも、全く同じ動きにはなりません。
JavaScript の || は falsy な値 (false, 0, "", null, undefined, NaN) をチェックします。
PHP の ?? は null のみをチェックします。
このあたりを更に細かく、違い見てみると下記のようになります。
// PHP での || の例
$result = "" || "default"; // true が代入される
$result = 0 || "default"; // true が代入される
$result = false || "default"; // true が代入される
// PHP での ?? の例
$result = "" ?? "default"; // "" (空文字)が代入される
$result = 0 ?? "default"; // 0 が代入される
$result = false ?? "default"; // false が代入される
$result = null ?? "default"; // "default" が代入される
// JavaScript での || の例
const result = "" || "default"; // "default" が代入される
const result = 0 || "default"; // "default" が代入される
const result = false || "default"; // "default" が代入される
擬似的にJavascriptの||と同じような挙動をempty()を使って再現できるのですが、これも厳密には一緒の動きにはなりません。詳しくは6.に追記しました。
2. 配列操作が違う!
PHPとJavaScriptの配列操作メソッドでは、元の配列を変更するかどうかについて違いがあります。
PHPの配列操作関数の特徴
PHPの多くの配列操作関数(array_map, array_filter, array_reduce など)は元の配列を変更せず、新しい配列を返します。
$numbers = [1, 2, 3];
// array_map は新しい配列を返し、元の配列は変更されない
$doubled = array_map(function($n) { return $n * 2; }, $numbers);
echo implode(',', $numbers); // 出力: 1,2,3
echo implode(',', $doubled); // 出力: 2,4,6
// array_filter も新しい配列を返す
$filtered = array_filter($numbers, function($n) { return $n > 1; });
echo implode(',', $numbers); // 出力: 1,2,3
echo implode(',', $filtered); // 出力: 2,3
ただし、元の配列を直接変更する関数もあります。
$numbers = [1, 2, 3];
// array_push は元の配列を変更する
array_push($numbers, 4);
echo implode(',', $numbers); // 出力: 1,2,3,4
// array_pop も元の配列を変更する
$last = array_pop($numbers);
echo implode(',', $numbers); // 出力: 1,2,3
JavaScriptの配列操作
JavaScriptでは、元の配列を変更するメソッドが多い傾向です。
元の配列を変更するメソッド:
const numbers = [1, 2, 3];
// push は元の配列を変更する
numbers.push(4);
console.log(numbers); // 出力: [1, 2, 3, 4]
// pop も元の配列を変更する
numbers.pop();
console.log(numbers); // 出力: [1, 2, 3]
// splice は元の配列を変更する
numbers.splice(1, 1);
console.log(numbers); // 出力: [1, 3]
// sort は元の配列を変更する
const fruits = ['banana', 'apple', 'orange'];
fruits.sort();
console.log(fruits); // 出力: ['apple', 'banana', 'orange']
// reverse も元の配列を変更する
numbers.reverse();
console.log(numbers); // 出力: [3, 1]
新しい配列を返すメソッド:
const numbers = [1, 2, 3];
// map は新しい配列を返す
const doubled = numbers.map(n => n * 2);
console.log(numbers); // 出力: [1, 2, 3]
console.log(doubled); // 出力: [2, 4, 6]
// filter も新しい配列を返す
const filtered = numbers.filter(n => n > 1);
console.log(numbers); // 出力: [1, 2, 3]
console.log(filtered); // 出力: [2, 3]
// slice は新しい配列を返す
const sliced = numbers.slice(1, 2);
console.log(numbers); // 出力: [1, 2, 3]
console.log(sliced); // 出力: [2]
主な違いのまとめ
- PHPの配列操作は関数として提供され、JavaScriptは配列のメソッドとして提供される
- PHPでは(Javascriptと比較して)多くの配列操作関数が元の配列を変更せずに、新しい配列を返す傾向がある
配列の操作はバグの原因となりやすい箇所なので、違いをしっかりと意識する必要がありそうです。
3. 変数スコープが広い
JavaScript で当たり前のように使っていたブロックスコープ。PHPではちょっと違います。
function test() {
if (true) {
$localVar = "ここだけ?"; //Javascriptの場合はifブロックの中だけでした参照できない
}
echo $localVar; // なんと出力される!
}
最初はバグかと思いましたが、これがPHPの仕様。関数スコープが基本となります。
Javascriptのvarと動きが近い感じですかね。
(ちょっと調べたら厳密には違いました。このあたり話が長くなるので端折ります。1次情報のリンクを張っておきますので興味がある方は下記を参照ください。)
参考:
PHP 変数のスコープ
Javascript var
4. 文字列連結は、(+)ではなく(.)を使う!
JavaScript では
let greeting = "Hello";
greeting += " World";
PHPでは
$greeting = "Hello";
$greeting .= " World"; // . を使う!
5. 非同期処理
JavaScript では当たり前のように使っていた Promise や async/await。PHPではそもそも考え方が違います。
JavaScript:非同期処理を意識して実装する必要がある。
async function getData() {
const response = await fetch('api/data');
const data = await response.json();
return data;
}
PHP:同期的に処理されるので非同期処理を意識せずに実装が可能。
function getData() {
// file_get_contents は同期的に処理されます
$response = file_get_contents('http://api.example.com');
// レスポンスが返ってくるまでここで処理がブロックされます
$data = json_decode($response, true);
return $data;
}
PHPのfile_get_contentsは、同期的に処理されます。PHP上で非同期的に処理するには、cURLを利用して処理を記載するか、ReactPHPやSwooleのようなフレームワークを利用したり、ポーリングの処理を実装したりする必要があるようです。(この先は並列処理して処理速度を上げたり、ユーザー体験を良くするなど非同期にする目的によっていろいろ変わりそうです。別途タイミング見つけて調査してまとめたいと思います。)
参考:
PHP file_get_contents
PHP cURL
6.空データの判定
1.の続きです。更に真偽値の判定に特化して見てみます。
// JavaScript
!""; // true
!"0"; // false
!0; // true
!"false"; // false
!false; // true
![].length; // true
// PHP
empty(""); // true
empty("0"); // true
empty(0); // true
empty("false"); // false
empty(false); // true
empty([]); // true
empty()を使えば、javascriptの||と同じ挙動をするユーザー関数を作れるかもと思って、上記を調べましたが、厳密にはempty()を使っても挙動は一緒になりませんでした。
7.エラーハンドリング
エラーが発生した際の記載の仕方が異なります。
// JavaScript
try {
throw new Error("Error");
} catch (e) {
console.error(e.message);
} finally {
// クリーンアップ処理
}
// PHP
try {
throw new Exception("Error");
} catch (Exception $e) {
echo $e->getMessage();
} finally {
// クリーンアップ処理
}
PHPの変数は型宣言しないのにエラー処理の場合、Exceptionと宣言するのが不思議です。
まとめ
PHPを始めて2週間が経ちましたが、まだまだ学ぶことばかりです。
これから PHP を始める JavaScript・PHPの初学者の方々の参考になれば幸いです!同じように躓いた方、別の発見があった方、コメントでぜひ教えてください!
追記
記事をまとめた後で、さらに以下のような注意点があることに気が付きました。このあたりは、また追ってまとめて行きたいとおもいます。
- PHPはリクエストごとに状態がリセットされる
- セッション管理の考え方がJavaScriptとは全く異なる
- 配列のキーは数値と文字列しか使えない(JavaScriptのようにオブジェクトは使えない)