はじめに
業務でPHPを触ることになったため、PHPについて調べてまとめてみました。
普段はPythonを使っているので、本記事ではPythonとPHPを比較しながら整理しています。
同じく「Pythonは触ってきたけど、これからPHPに入る」という方の参考になれば嬉しいです。
この記事のコード比較は、次のバージョンで確認しています。
- Python
3.13 - PHP
8.4
目次
1. 基本構造
1-1. PHPタグ
PHPコードは <?php で開始します。
この開始タグがないと、ファイル内のコードはPHPとして実行されません。
?> は「PHPコード終了タグ」ですが、終了タグの後に余分な空白や改行があると、予期せぬ挙動を引き起こす場合があるため、ファイルがPHPコードで終わる場合は通常省略するようです。
<?php
echo "hello" . PHP_EOL;
1-2. コメント
Pythonは#や""" """, ''' '''でコメントを記載しますが、PHPでは//、#, /* */でコメントを書きます。
#よりも//が推奨されるようです。
// PHP: 1行コメント
# PHP: 1行コメント(こちらでも可)
/*
PHP: 複数行コメント
*/
1-3. 文の終端(セミコロン)
Pythonでは文末に;等は必要ないですが、
PHPは文末に;が必要です。
※ PHP_EOLは改行を表す定数です
$name = 'alice';
echo $name . PHP_EOL;
1-4. 文字列リテラル(シングル/ダブルクオート)
Pythonの'と"は基本的に同じ意味を持ち、可読性や文字列内の引用符の有無で使い分けたりしますが、
PHPの場合'はそのまま文字列、"は変数展開ありの場合で使い分けたりします。
$name = 'alice';
echo 'hello $name' . PHP_EOL; // hello $name
echo "hello $name" . PHP_EOL; // hello alice
2. 変数
PHPの真偽値はtrue / false、PythonのNoneに相当する値はnullです。
(Pythonと違い、大文字小文字の両方でも定義可能ですが、小文字で統一が一般的のようです。)
name = "alice"
age = 30
is_active = True
note = None
$name = 'alice';
$age = 30;
$isActive = true; // Trueでも定義可能
$note = null; // Nullでも定義可能
3. 定数
定数はconstを使って定義します。
Pythonと同じようにUPPER_SNAKE_CASEで定義するのが一般的です。
const MAX_RETRY = 3;
const DEFAULT_REGION = 'ap-northeast-1';
4. 配列(データ構造)
Pythonでは[]と{}でlistとdictが分かれていますが、PHPではarrayでlistとdict両方を定義して扱うようです。
※ var_dumpは、変数の中身と型を詳しく表示するPHPのデバッグ関数です。
scores = [88, 92, 75]
profile = {
"name": "alice",
"language": "python",
}
$scores = [88, 92, 75];
$profile = [
'name' => 'alice',
'language' => 'php',
];
var_dump($scores);
/*
array(3) {
[0]=>
int(88)
[1]=>
int(92)
[2]=>
int(75)
}
*/
var_dump($profile);
/*
array(2) {
["name"]=>
string(5) "alice"
["language"]=>
string(3) "php"
}
*/
5. 制御構造
5-1. if / elseif
PHPではelifでなく、elseifになるのが注意点です。
score = 80
if score >= 80:
print("great")
elif score >= 60:
print("good")
else:
print("bad")
# great
$score = 80;
if ($score >= 80) {
echo 'great';
} elseif ($score >= 60) {
echo 'good';
} else {
echo 'bad';
}
// great
5-2. for / foreach / while / do-while
do-whileは条件判定の前に処理を1回実行します(最低1回は実行される)。
Pythonには do-while 構文がないため、while True + break で近い挙動を作ります。
forとforeachの使い分け
-
for: インデックスを使って制御したい際に向いている -
foreach: 配列の各要素を順番に取り出す際に向いている
values = [10, 20, 30]
for v in values:
print(v)
i = 0
while i < len(values):
print(values[i])
i += 1
j = 0
while True:
print(values[j])
j += 1
if j >= len(values):
break
$values = [10, 20, 30];
for ($i = 0; $i < count($values); $i++) {
echo $values[$i] . PHP_EOL;
}
foreach ($values as $v) {
echo $v . PHP_EOL;
}
$i = 0;
while ($i < count($values)) {
echo $values[$i] . PHP_EOL;
$i++;
}
$j = 0;
do {
echo $values[$j] . PHP_EOL;
$j++;
} while ($j < count($values));
5-3. switch / match
注意点
-
matchはPHP8以上のバージョンで使用可能な分岐構文です。 - switchは文(statement), matchは式(expression)
- switch文は弱い比較(==)でmatchは厳密に値を比較(===)します。
state = "reading"
# switch(if/elifで表現しました)
if state in ("unread", "queued"):
label_by_if = "未着手"
elif state == "reading":
label_by_if = "読書中"
elif state == "finished":
label_by_if = "読了"
else:
label_by_if = "不明"
print(label_by_if)
# match
match state:
case "unread" | "queued":
label_by_match = "未着手"
case "reading":
label_by_match = "読書中"
case "finished":
label_by_match = "読了"
case _:
label_by_match = "不明"
print(label_by_match)
$status = 'reading';
// switch
switch ($status) {
case 'unread':
case 'queued': // 複数ケースをまとめる
$labelBySwitch = '未着手';
break;
case 'reading':
$labelBySwitch = '読書中';
break;
case 'finished':
$labelBySwitch = '読了';
break;
default:
$labelBySwitch = '不明';
}
echo $labelBySwitch . PHP_EOL;
// match
$labelByMatch = match ($status) {
'unread', 'queued' => '未着手', // 複数ケースをカンマでまとめられる
'reading' => '読書中',
'finished' => '読了',
default => '不明',
};
echo $labelByMatch . PHP_EOL;
6. 関数
補足: php8以上であれば、greet(name: 'alice')のように名前付き引数で渡すことができるようです。
def greet(name: str, prefix: str = "Hello") -> str:
return f"{prefix}, {name}"
def sum_all(*args: int) -> int:
return sum(args)
def print_options(**kwargs: str) -> None:
for key, value in kwargs.items():
print(f"{key}={value}")
function greet(string $name, string $prefix = 'Hello'): string
{
return "{$prefix}, {$name}";
}
// 可変長引数(Pythonの*argsに近い)
function sumAll(int ...$numbers): int
{
return array_sum($numbers);
}
function printOptions(string ...$options): void
{
foreach ($options as $key => $value) {
echo "{$key}={$value}" . PHP_EOL;
}
}
echo greet('alice') . PHP_EOL; // Hello, alice
echo sumAll(1, 2, 3, 4) . PHP_EOL; // 10
printOptions(
env: 'dev',
region: 'ap-northeast-1',
);
// env=dev
// region=ap-northeast-1
7. クラスとオブジェクト
サンプル例では継承・クラス変数・classmethod・staticmethod・継承禁止等を比較するために書いたサンプルコードです。
補足:
- Pythonの
_methodや__methodは慣習によるアクセス制御であり、
PHPのprotected / privateのように言語仕様として強制されるものではありません。
class Person:
"""人を表す基底クラス"""
species = "human"
def __init__(self, name: str, age: int):
"""
Args:
name: 名前
age: 年齢
"""
self.name = name
self.age = age
def introduce(self) -> None:
"""自己紹介"""
print(f"{self.name}です。{self.age}歳です。")
def _log(self) -> None:
"""内部用メソッド"""
print("ログ出力")
def __secret(self) -> None:
"""クラス内部専用メソッド"""
print("秘密")
@staticmethod
def greeting():
"""共通の挨拶"""
print("こんにちは")
class Student(Person):
"""学生クラス"""
school_type = "university"
def __init_subclass__(cls, **kwargs):
raise TypeError("Studentクラスは継承できません")
def __init__(self, name: str, age: int, school: str):
super().__init__(name, age)
self.school = school
def study(self) -> None:
"""勉強する"""
print(f"{self.name}は{self.school}で勉強しています。")
def introduce(self) -> None:
"""自己紹介(オーバーライド)"""
print(f"{self.name}です。{self.school}の学生です。")
@classmethod
def create_guest_student(cls):
"""ゲスト学生を生成する"""
return cls("ゲスト学生", 18, "未設定")
student = Student("太郎", 20, "東京大学")
student.introduce() # 太郎です。東京大学の学生です。
student.study() # 太郎は東京大学で勉強しています。
guest = Student.create_guest_student()
guest.introduce() # ゲスト学生です。未設定の学生です。
Person.greeting() # こんにちは
print(Person.species) # human
print(Student.school_type) # university
class Person
{
public static string $species = "human";
public string $name;
public int $age;
public function __construct(string $name, int $age)
{
$this->name = $name;
$this->age = $age;
}
public function introduce(): void
{
echo "{$this->name}です。{$this->age}歳です。\n";
}
protected function log(): void
{
echo "ログ出力\n";
}
private function secret(): void
{
echo "秘密\n";
}
public static function greeting(): void
{
echo "こんにちは\n";
}
}
final class Student extends Person
{
public static string $schoolType = "university";
public string $school;
public function __construct(string $name, int $age, string $school)
{
parent::__construct($name, $age);
$this->school = $school;
}
public function study(): void
{
echo "{$this->name}は{$this->school}で勉強しています。\n";
}
public function introduce(): void
{
echo "{$this->name}です。{$this->school}の学生です。\n";
}
public static function createGuestStudent(): Student
{
return new Student("ゲスト学生", 18, "未設定");
}
}
$student = new Student("太郎", 20, "東京大学");
$student->introduce(); // 太郎です。東京大学の学生です。
$student->study(); // 太郎は東京大学で勉強しています。
$guest = Student::createGuestStudent();
$guest->introduce(); // ゲスト学生です。未設定の学生です。
Person::greeting(); // こんにちは
echo Person::$species . "\n"; // human
echo Student::$schoolType . "\n"; // university
8. 名前空間
Pythonでは import を使ってモジュールを読み込みます。
一方、PHPでは自作クラスなどはComposerのオートロード機能によって
自動的に読み込まれることが一般的です。
そのためPHPのuseはPythonのimportのようにモジュールを読み込むものではなく、クラスのフルネームを短く書くためのエイリアスとして使われます。
PHPではPSR-4というオートロード規約に従い、「ファイル名 = クラス名」にするのが一般的で、一方、実行用のファイル(例: index.php, main.php)などは小文字でファイル名を命名するのが一般的のようです。
# app/services/user.pyに定義したとする
class UserService:
def hello(self) -> None:
print("hello")
# app/main.pyでUserServiceを呼び出した場合。
# また、比較ように標準モジュールのosを使用する場合
import os
from services.user import UserService
default_region = os.getenv("DEFAULT_REGION", "ap-northeast-1")
user_service = UserService()
user_service.hello() # hello
# app/Services/UserService.phpに定義したとする
namespace App\Services;
class UserService
{
public function hello(): void
{
echo 'hello' . PHP_EOL;
}
}
# app/main.phpでUserServiceを呼び出した場合。
# また、比較ように標準モジュールのgetenvを使用する場合
use App\Services\UserService;
$defaultRegion = getenv("DEFAULT_REGION") ?: "ap-northeast-1";
$service = new UserService();
$service->hello(); // hello
# または、useを使用せずにフルパスで書くこともできる
$service = new \App\Services\UserService();
$service->hello(); // hello
9. 例外処理
補足:
Pythonにはtryのelse節がありますが、PHPにはありません。
PHPでは例外が発生しなかった場合の処理は try/catch の後に書きます。
try:
raise RuntimeError("テストエラー")
except Exception as e:
print("例外時に呼び出し")
raise # RuntimeErrorをそのまま再送出
else:
print("例外が発生しなかった場合だけ実行")
finally:
print("例外の有無に関わらず最後に実行")
try {
throw new RuntimeException('テストエラー');
} catch (Exception $e) {
echo '例外時に呼び出し' . PHP_EOL;
throw $e; // RuntimeExceptionをそのまま再送出
} finally {
echo '例外の有無に関わらず最後に実行' . PHP_EOL;
}
echo '例外が発生しなかった場合だけ実行';
参考
