PHP · 심층 가이드
PHP 기초 문법 완전 정리
느슨한 비교와 점진적 타입 선언이 뒤섞인 PHP 문법을 strict_types, match, enum, readonly 같은 최신 도구 중심으로 정리해 예측 가능한 코드를 쓰도록 만듭니다.
PHP의 문법을 어렵게 만드는 건 문법 자체가 아니라 두 시대가 한 파일에 공존한다는 사실입니다. 값은 여전히 동적으로 타입이 바뀌지만, 파라미터와 반환값에는 타입을 선언할 수 있고 그 선언이 실제로 강제되는지는 파일 맨 위의 declare(strict_types=1) 한 줄에 달려 있습니다. 이 선언은 파일 단위로만 적용되며 호출하는 쪽 기준으로 동작합니다. 이 구조를 모르면 같은 함수가 어떤 파일에서는 문자열 "3"을 순순히 정수로 받고 다른 파일에서는 TypeError를 던지는 상황을 이해할 수 없습니다.
그래서 strict_types 선언을 변수와 기본 타입 바로 다음에 배치했습니다. 여기서 타입 경계를 정한 뒤 union 타입과 nullable 타입으로 표현 범위를 넓히고, 값이 없을 때의 처리를 null coalescing 사용과 nullsafe 연산자로 이어 가면 됩니다. 분기 쪽은 if elseif else 조건문에서 시작하되 반환값이 필요한 분기는 match 표현식으로 옮기고, 고정된 후보 집합이 보이면 enum 기본 사용으로 승격시키는 흐름이 자연스럽습니다.
비교 연산자에서 한 번은 반드시 데입니다. match는 항상 === 로 판정하지만 switch는 == 로 판정하므로, 같은 조건표를 옮겨 적었는데 결과가 달라질 수 있습니다. 또 ?? 는 isset() 기준이라 값이 null일 때만 폴백하고, ?: 는 빈 문자열이나 0 에도 폴백합니다. readonly 프로퍼티는 초기화 후 재할당이 막히는데, 배열을 담아 두면 원소 하나를 바꾸는 것조차 재할당으로 간주되어 거부됩니다.
01변수와 기본 타입
문자열, 정수, 불리언 등 PHP의 기본 타입을 확인하는 예제입니다.
PHP code
<?php
$name = 'PHP';
$year = 2026;
$isStable = true;
var_dump($name, $year, $isStable);예제 결과를 볼 때는 값뿐 아니라 타입도 같이 확인하는 습관을 들이면 PHP의 타입 시스템을 훨씬 안정적으로 익힐 수 있습니다.
느슨한 비교와 묵시적 타입 변환이 섞이면 예상과 다른 결과가 나올 수 있습니다.
02strict_types 선언
파일 단위 strict_types 선언으로 더 엄격한 타입 체크를 적용하는 예제입니다.
PHP code
<?php
declare(strict_types=1);
function add(int $a, int $b): int {
return $a + $b;
}
echo add(1, 2);예제 결과를 볼 때는 값뿐 아니라 타입도 같이 확인하는 습관을 들이면 PHP의 타입 시스템을 훨씬 안정적으로 익힐 수 있습니다.
strict_types는 선언한 파일에만 적용되므로 프로젝트 전체가 자동으로 엄격해지지는 않습니다.
03const 와 define 사용
상수를 선언하는 두 가지 대표 방식인 const와 define을 비교하는 예제입니다.
PHP code
<?php
const APP_NAME = 'DevToolsVerse';
define('APP_STAGE', 'local');
echo APP_NAME . ' / ' . APP_STAGE;예제 결과를 볼 때는 값뿐 아니라 타입도 같이 확인하는 습관을 들이면 PHP의 타입 시스템을 훨씬 안정적으로 익힐 수 있습니다.
느슨한 비교와 묵시적 타입 변환이 섞이면 예상과 다른 결과가 나올 수 있습니다.
04숫자 구분자와 산술
가독성을 높이는 숫자 구분자와 기본 산술 연산을 익히는 예제입니다.
PHP code
<?php
$budget = 1_500_000;
$taxRate = 0.1;
echo $budget + ($budget * $taxRate);예제 결과를 볼 때는 값뿐 아니라 타입도 같이 확인하는 습관을 들이면 PHP의 타입 시스템을 훨씬 안정적으로 익힐 수 있습니다.
느슨한 비교와 묵시적 타입 변환이 섞이면 예상과 다른 결과가 나올 수 있습니다.
05문자열 보간과 연결
이중 따옴표 보간과 점 연산자 연결을 함께 보는 예제입니다.
PHP code
<?php
$name = 'pullh';
echo "hello {$name}
";
echo 'hello ' . $name . "
";예제 결과를 볼 때는 값뿐 아니라 타입도 같이 확인하는 습관을 들이면 PHP의 타입 시스템을 훨씬 안정적으로 익힐 수 있습니다.
느슨한 비교와 묵시적 타입 변환이 섞이면 예상과 다른 결과가 나올 수 있습니다.
06mb_strlen 으로 길이 재기
멀티바이트 문자열 길이를 안전하게 재는 예제입니다.
PHP code
<?php
$text = '안녕하세요 PHP';
echo mb_strlen($text, 'UTF-8');예제 결과를 볼 때는 값뿐 아니라 타입도 같이 확인하는 습관을 들이면 PHP의 타입 시스템을 훨씬 안정적으로 익힐 수 있습니다.
strlen은 바이트 길이를 반환하므로 UTF-8 한글 길이를 셀 때 그대로 쓰면 틀릴 수 있습니다.
07배열 구조 분해
짧은 문법으로 배열 값을 변수에 분해하는 예제입니다.
PHP code
<?php
[$first, $second] = ['php', 'laravel'];
echo $first . ' / ' . $second;예제 결과를 볼 때는 값뿐 아니라 타입도 같이 확인하는 습관을 들이면 PHP의 타입 시스템을 훨씬 안정적으로 익힐 수 있습니다.
느슨한 비교와 묵시적 타입 변환이 섞이면 예상과 다른 결과가 나올 수 있습니다.
08if elseif else 조건문
PHP의 기본 분기문 구조를 가장 단순한 형태로 확인하는 예제입니다.
PHP code
<?php
$score = 87;
if ($score >= 90) {
echo 'A';
} elseif ($score >= 80) {
echo 'B';
} else {
echo 'C';
}예제 결과를 볼 때는 값뿐 아니라 타입도 같이 확인하는 습관을 들이면 PHP의 타입 시스템을 훨씬 안정적으로 익힐 수 있습니다.
느슨한 비교와 묵시적 타입 변환이 섞이면 예상과 다른 결과가 나올 수 있습니다.
09match 표현식
PHP 8의 match 표현식으로 분기를 값으로 돌려받는 예제입니다.
PHP code
<?php
$code = 404;
$message = match ($code) {
200 => 'ok',
404 => 'not found',
default => 'unknown',
};
echo $message;예제 결과를 볼 때는 값뿐 아니라 타입도 같이 확인하는 습관을 들이면 PHP의 타입 시스템을 훨씬 안정적으로 익힐 수 있습니다.
match는 느슨한 비교가 아니라 엄격한 비교를 사용하므로 case 매칭 규칙이 switch와 다릅니다.
10for 와 while 반복문
for와 while을 나란히 실행하며 반복문 기본 형태를 비교하는 예제입니다.
PHP code
<?php
for ($i = 0; $i < 3; $i++) {
echo "for: {$i}
";
}
$n = 0;
while ($n < 3) {
echo "while: {$n}
";
$n++;
}예제 결과를 볼 때는 값뿐 아니라 타입도 같이 확인하는 습관을 들이면 PHP의 타입 시스템을 훨씬 안정적으로 익힐 수 있습니다.
느슨한 비교와 묵시적 타입 변환이 섞이면 예상과 다른 결과가 나올 수 있습니다.
11foreach 키와 값 순회
연관 배열을 키와 값으로 순회하는 PHP의 대표 패턴입니다.
PHP code
<?php
$langs = ['backend' => 'php', 'ml' => 'python'];
foreach ($langs as $key => $value) {
echo "{$key}: {$value}
";
}예제 결과를 볼 때는 값뿐 아니라 타입도 같이 확인하는 습관을 들이면 PHP의 타입 시스템을 훨씬 안정적으로 익힐 수 있습니다.
느슨한 비교와 묵시적 타입 변환이 섞이면 예상과 다른 결과가 나올 수 있습니다.
12null coalescing 사용
값이 없을 때 기본값으로 안전하게 대체하는 null coalescing 예제입니다.
PHP code
<?php
$name = $_GET['name'] ?? 'guest';
echo $name;예제 결과를 볼 때는 값뿐 아니라 타입도 같이 확인하는 습관을 들이면 PHP의 타입 시스템을 훨씬 안정적으로 익힐 수 있습니다.
느슨한 비교와 묵시적 타입 변환이 섞이면 예상과 다른 결과가 나올 수 있습니다.
13nullsafe 연산자
객체가 null일 수 있을 때 안전하게 속성을 읽는 nullsafe 연산자 예제입니다.
PHP code
<?php
class Profile {
public function __construct(public ?string $bio) {}
}
$profile = new Profile('php learner');
echo $profile?->bio ?? 'empty';예제 결과를 볼 때는 값뿐 아니라 타입도 같이 확인하는 습관을 들이면 PHP의 타입 시스템을 훨씬 안정적으로 익힐 수 있습니다.
느슨한 비교와 묵시적 타입 변환이 섞이면 예상과 다른 결과가 나올 수 있습니다.
14enum 기본 사용
PHP 8.1 enum을 사용해 문자열 상태를 타입 안전하게 표현하는 예제입니다.
PHP code
<?php
enum Status: string {
case Draft = 'draft';
case Published = 'published';
}
echo Status::Draft->value;예제 결과를 볼 때는 값뿐 아니라 타입도 같이 확인하는 습관을 들이면 PHP의 타입 시스템을 훨씬 안정적으로 익힐 수 있습니다.
느슨한 비교와 묵시적 타입 변환이 섞이면 예상과 다른 결과가 나올 수 있습니다.
15readonly 프로퍼티
생성 후 변경되면 안 되는 값을 readonly 프로퍼티로 고정하는 예제입니다.
PHP code
<?php
class User {
public function __construct(public readonly int $id) {}
}
$user = new User(7);
echo $user->id;예제 결과를 볼 때는 값뿐 아니라 타입도 같이 확인하는 습관을 들이면 PHP의 타입 시스템을 훨씬 안정적으로 익힐 수 있습니다.
readonly 프로퍼티는 생성자 이후 다시 대입할 수 없습니다.
16Attribute 선언과 읽기
PHP Attribute와 Reflection을 조합해 메타데이터를 읽는 예제입니다.
PHP code
<?php
#[Attribute]
class Route {
public function __construct(public string $path) {}
}
#[Route('/users')]
class UserController {}
$ref = new ReflectionClass(UserController::class);
echo $ref->getAttributes(Route::class)[0]->newInstance()->path;예제 결과를 볼 때는 값뿐 아니라 타입도 같이 확인하는 습관을 들이면 PHP의 타입 시스템을 훨씬 안정적으로 익힐 수 있습니다.
느슨한 비교와 묵시적 타입 변환이 섞이면 예상과 다른 결과가 나올 수 있습니다.
17이름 있는 인자 호출
긴 인자 목록을 더 읽기 쉽게 호출하는 이름 있는 인자 예제입니다.
PHP code
<?php
function createUser(string $name, string $role = 'member', bool $active = true): array {
return compact('name', 'role', 'active');
}
print_r(createUser(name: 'mida', active: false));예제 결과를 볼 때는 값뿐 아니라 타입도 같이 확인하는 습관을 들이면 PHP의 타입 시스템을 훨씬 안정적으로 익힐 수 있습니다.
느슨한 비교와 묵시적 타입 변환이 섞이면 예상과 다른 결과가 나올 수 있습니다.
18union 타입과 nullable 타입
union 타입과 nullable 타입을 한 함수에서 함께 사용하는 예제입니다.
PHP code
<?php
function normalize(int|string|null $value): ?string {
return $value === null ? null : trim((string) $value);
}
var_dump(normalize(10));예제 결과를 볼 때는 값뿐 아니라 타입도 같이 확인하는 습관을 들이면 PHP의 타입 시스템을 훨씬 안정적으로 익힐 수 있습니다.
느슨한 비교와 묵시적 타입 변환이 섞이면 예상과 다른 결과가 나올 수 있습니다.
19DateTimeImmutable 사용
상태 변경이 없는 DateTimeImmutable로 날짜 계산을 안전하게 처리하는 예제입니다.
PHP code
<?php
$date = new DateTimeImmutable('2026-03-28 09:00:00');
$next = $date->modify('+7 days');
echo $date->format('Y-m-d') . ' -> ' . $next->format('Y-m-d');예제 결과를 볼 때는 값뿐 아니라 타입도 같이 확인하는 습관을 들이면 PHP의 타입 시스템을 훨씬 안정적으로 익힐 수 있습니다.
느슨한 비교와 묵시적 타입 변환이 섞이면 예상과 다른 결과가 나올 수 있습니다.
20null coalescing assignment
키가 없을 때만 기본값을 주입하는 null coalescing assignment 예제입니다.
PHP code
<?php
$config = [];
$config['cache_ttl'] ??= 300;
echo $config['cache_ttl'];예제 결과를 볼 때는 값뿐 아니라 타입도 같이 확인하는 습관을 들이면 PHP의 타입 시스템을 훨씬 안정적으로 익힐 수 있습니다.
느슨한 비교와 묵시적 타입 변환이 섞이면 예상과 다른 결과가 나올 수 있습니다.
21참조 전달 기본
PHP 참조의 기본 동작을 가장 작은 예제로 이해하는 코드입니다.
PHP code
<?php
$count = 1;
$alias =& $count;
$alias++;
echo $count;예제 결과를 볼 때는 값뿐 아니라 타입도 같이 확인하는 습관을 들이면 PHP의 타입 시스템을 훨씬 안정적으로 익힐 수 있습니다.
참조는 편리하지만 데이터 흐름을 흐리기 쉬우므로 꼭 필요한 경우에만 사용하는 편이 좋습니다.
22filter_var 로 입력 검증
입력값을 직접 파싱하기보다 filter_var로 기본 검증을 수행하는 예제입니다.
PHP code
<?php
$email = 'team@pullh.com';
var_dump(filter_var($email, FILTER_VALIDATE_EMAIL) !== false);예제 결과를 볼 때는 값뿐 아니라 타입도 같이 확인하는 습관을 들이면 PHP의 타입 시스템을 훨씬 안정적으로 익힐 수 있습니다.
느슨한 비교와 묵시적 타입 변환이 섞이면 예상과 다른 결과가 나올 수 있습니다.
23anonymous class 빠르게 만들기
가벼운 테스트용 객체나 어댑터를 익명 클래스로 빠르게 만드는 예제입니다.
PHP code
<?php
$logger = new class {
public function info(string $message): void {
echo "[info] {$message}
";
}
};
$logger->info('boot');예제 결과를 볼 때는 값뿐 아니라 타입도 같이 확인하는 습관을 들이면 PHP의 타입 시스템을 훨씬 안정적으로 익힐 수 있습니다.
느슨한 비교와 묵시적 타입 변환이 섞이면 예상과 다른 결과가 나올 수 있습니다.
24Closure 에 값 캡처하기
익명 함수가 외부 스코프의 값을 use로 캡처하는 예제입니다.
PHP code
<?php
$prefix = 'php';
$formatter = function (string $value) use ($prefix): string {
return $prefix . ':' . $value;
};
echo $formatter('8.3');예제 결과를 볼 때는 값뿐 아니라 타입도 같이 확인하는 습관을 들이면 PHP의 타입 시스템을 훨씬 안정적으로 익힐 수 있습니다.
느슨한 비교와 묵시적 타입 변환이 섞이면 예상과 다른 결과가 나올 수 있습니다.
25우주선 연산자
정렬 비교 함수에서 자주 쓰는 우주선 연산자 사용 예제입니다.
PHP code
<?php
$a = 10;
$b = 20;
echo $a <=> $b;예제 결과를 볼 때는 값뿐 아니라 타입도 같이 확인하는 습관을 들이면 PHP의 타입 시스템을 훨씬 안정적으로 익힐 수 있습니다.
느슨한 비교와 묵시적 타입 변환이 섞이면 예상과 다른 결과가 나올 수 있습니다.
정리하며
- 새 파일은
declare(strict_types=1)로 시작해 암묵적 형변환을 끊습니다. - 값을 돌려주는 분기는 switch 대신 match를 써서 엄격 비교와 누락 검출을 얻습니다.
??는 null만,?:는 falsy 전체를 걸러 낸다는 차이를 구분합니다.- 고정된 상태 후보는 문자열 상수 대신 enum으로 선언해 오타를 컴파일 시점에 잡습니다.
더 깊이 들어가고 싶다면 PHP 학습 라이브러리에서 다른 주제 가이드를 이어서 보거나, 언어 비교에서 같은 개념이 다른 언어에서 어떻게 표현되는지 확인해 보세요.