PHP가 동적 프로퍼티를 제거했습니다. 이유와 해결 방법
Source: Dev.to
위 링크에 있는 전체 글을 제공해 주시면, 해당 내용을 한국어로 번역해 드리겠습니다. (코드 블록, URL 및 마크다운 형식은 그대로 유지됩니다.)
동적 속성이란?
동적 속성은 클래스에 선언되지 않은 상태에서 객체에 추가되는 속성입니다.
class User {}
$user = new User();
$user->age = 30; // ⚠️ Dynamic property
The Problem
Silent Typos → Hidden Bugs
$user->custmer_email = 'john@example.com'; // Typo!
echo $user->customer_email; // null 😱
No Type Safety
$user->age = 30; // int
$user->age = 'thirty'; // string – no error
$user->age = null; // also "fine"
IDE & Tooling Limitations
- 자동 완성 없음
- 리팩터링 지원 없음
- 정적 분석 도구가 문제를 감지하지 못함
Architecture Decay
// Anemic domain model
$user->cached_data = $something;
$user->temp_flag = true;
$user->random_stuff = $whatever;
해결책: 속성을 명시적으로 선언하기
최신 PHP 방식
class User {
public string $name;
public int $age;
public bool $isActive;
}
생성자 속성 승격 (PHP 8+)
class CreateOrderRequest {
public function __construct(
public readonly string $customerId,
public readonly array $items,
public readonly ?string $promoCode = null,
) {}
}
Laravel‑특화 솔루션
❌ 정의되지 않은 속성을 직접 추가하지 마세요
$user = User::find(1);
$user->is_verified = true; // Undefined property!
✅ Fillable 속성과 Cast 정의하기
class User extends Model {
protected $fillable = [
'name',
'email',
'is_verified',
];
protected $casts = [
'is_verified' => 'boolean',
'email_verified_at'=> 'datetime',
];
}
✅ 접근자(Accessor) 사용하기
use Illuminate\Database\Eloquent\Casts\Attribute;
class User extends Model {
protected function fullName(): Attribute {
return Attribute::make(
get: fn() => "{$this->first_name} {$this->last_name}",
);
}
}
탈출구 (절제해서 사용!)
#[\AllowDynamicProperties]
class LegacyCode {
// Opt into old behavior
}
이것은 다음 경우에만 사용하세요:
- 레거시 벤더 코드
- 단계적 마이그레이션 (제거 계획 포함)
새로운 코드에는 절대 사용하지 마세요.
Migration Strategy
-
Run Static Analysis
./vendor/bin/phpstan analyze --level 8 ./vendor/bin/psalm --no-cache -
Fix Systematically
- Critical – Domain models (fix now)
- High – Controllers / Services (next sprint)
- Medium – DTOs (incremental)
- Low – Test utilities (add attribute if needed)
-
Prevent Regression
Add static analysis to CI:
# .github/workflows/ci.yml - name: Static Analysis run: ./vendor/bin/phpstan analyze
Version Status
| PHP 버전 | 상태 |
|---|---|
| 8.1 | ✅ 허용 |
| 8.2 | ⚠️ 사용 중단 (경고) |
| 9.0 | ❌ 치명적 오류 (제거됨) |
왜 이 변경이 중요한가
현대 PHP는 다음을 지향하고 있습니다:
- 암시보다 명시
- 기본적인 타입 안전성
- 향상된 도구 지원
- 리팩터링에 친화적인 코드
동적 프로퍼티는 이러한 목표와 충돌합니다.
실제 예시: 전과 후
이전 ❌
class ApiResponse {}
$response = new ApiResponse();
$response->data = $data;
$response->status = 200;
이후 ✅
class ApiResponse {
public function __construct(
public readonly mixed $data,
public readonly int $status,
public readonly int $timestamp = time(),
) {}
}
$response = new ApiResponse($data, 200);
이점: 타입 안전, 자체 문서화, 불변성, 그리고 리팩터링에 용이함.
주요 내용
- 모든 속성을 명시적으로 선언합니다.
- 안전성을 위해 타입이 지정된 속성을 사용합니다.
- PHP 8+ 기능(생성자 프로모션,
readonly)을 활용합니다. - 정적 분석을 정기적으로 실행합니다.
- 레거시 코드를 위한 마이그레이션 경로를 계획합니다.
전체 가이드—역사적 배경, 상세 마이그레이션 전략, 고급 Laravel 패턴 등—는 Medium 원문을 읽어보세요:
The Death of Dynamic Properties in PHP – A Deep Dive into Modern Object Design