PHP V5의 새로운 언어 기능을 사용하면 코드의 유지 관리성과 안정성이 크게 향상될 수 있습니다. 이 기사를 읽으면 이러한 새로운 기능을 활용하여 PHP V4에서 개발된 코드를 PHP V5로 마이그레이션하는 방법을 배울 수 있습니다.
PHP V5는 PHP V4를 기반으로 상당한 개선을 이루었습니다. 새로운 언어 기능을 사용하면 신뢰할 수 있는 클래스 라이브러리를 보다 쉽게 구축하고 유지 관리할 수 있습니다. 또한, 표준 라이브러리를 다시 작성함으로써 PHP가 Java™ 프로그래밍 언어와 같은 동료 웹 관용어와 더욱 일치하도록 만들었습니다. PHP의 새로운 객체 지향 기능 중 일부를 살펴보고 기존 PHP V4 코드를 PHP V5로 마이그레이션하는 방법을 알아보겠습니다.
먼저, 새로운 언어 기능과 PHP 제작자가 PHP V4를 사용하여 객체를 생성하는 방식을 어떻게 변경했는지 살펴보겠습니다. V5의 아이디어는 웹 애플리케이션 개발을 위한 업계 최고의 언어를 만드는 것이었습니다. 이는 PHP V4의 한계를 이해한 다음 다른 언어(예: Java, C#, C++, Ruby 및 Perl)에서 알려진 좋은 언어 아키텍처를 추출하여 PHP에 통합하는 것을 의미합니다.
첫 번째이자 가장 중요한 새 기능은 클래스 메서드와 인스턴스 변수(공개, 보호 및 비공개 키워드)에 대한 액세스 보호입니다. 이 새로운 기능을 사용하면 클래스 디자이너는 클래스의 고유 속성에 대한 제어를 유지하면서 사용자에게 클래스에 액세스할 수 있는 클래스와 액세스할 수 없는 클래스를 알려줄 수 있습니다.
PHP V4에서는 모든 코드가 공개됩니다. PHP V5에서 클래스 디자이너는 어떤 코드가 외부 세계에 표시되는지(공개), 어떤 코드가 클래스 내부에만 표시되는지(비공개) 또는 클래스의 하위 클래스에만 표시되는지(보호됨) 선언할 수 있습니다. 이러한 액세스 제어가 없으면 대규모 팀에서 코드를 개발하거나 코드를 라이브러리로 배포하는 것이 방해됩니다. 해당 클래스의 사용자가 전용 멤버 변수여야 하는 액세스 코드나 잘못된 메서드를 사용할 가능성이 높기 때문입니다.
또 다른 큰 새로운 기능은 계약 프로그래밍을 허용하는 키워드 인터페이스와 요약입니다. 계약 프로그래밍은 한 클래스가 다른 클래스에 계약을 제공한다는 것을 의미합니다. 즉, "이것이 내가 하려는 일이며, 여러분은 이것이 어떻게 수행되는지 알 필요가 없습니다." 인터페이스를 구현하는 모든 클래스는 이 계약을 준수합니다. 인터페이스의 모든 사용자는 인터페이스에 지정된 방법만 사용하는 데 동의합니다. 나중에 설명하겠지만 abstract 키워드를 사용하면 인터페이스 작업이 매우 쉬워집니다.
액세스 제어와 계약 프로그래밍이라는 두 가지 주요 기능을 사용하면 대규모 코더 팀이 대규모 코드 기반을 보다 원활하게 작업할 수 있습니다. 또한 이러한 기능을 통해 IDE는 보다 풍부한 언어 지능형 기능 세트를 제공할 수 있습니다. 이 문서에서는 여러 마이그레이션 문제를 다룰 뿐만 아니라 이러한 새로운 주요 언어 기능을 사용하는 방법을 설명하는 데에도 시간을 할애합니다.
액세스 제어
새로운 언어 기능을 보여주기 위해 Configuration이라는 클래스를 사용했습니다. 이 간단한 클래스에는 웹 애플리케이션에 대한 구성 항목(예: 이미지 디렉터리 경로)이 포함되어 있습니다. 이상적으로는 이 정보가 파일이나 데이터베이스에 상주합니다. 목록 1은 단순화된 버전을 보여줍니다.
목록 1. access.php4
<?php
클래스 구성
{
var $_items = array();
함수 구성() {
$this->_items[ 'imgpath' ] = '이미지';
}
함수 get( $key ) {
$this->_items[ $key ]를 반환합니다.
}
}
$c = 새로운 구성();
echo( $c->get( 'imgpath' )."n" );
?>
이것은 완전히 정통적인 PHP V4 클래스입니다. 멤버 변수는 구성 항목 목록을 보유하고 생성자는 항목을 로드하며 get()이라는 액세스 메서드는 항목 값을 반환합니다.
스크립트를 실행하면 명령줄에 다음 코드가 나타납니다:
%php access.php4
이미지
%
매우 좋은! 이 결과는 코드가 정상적으로 실행되고 imgpath 구성 항목의 값이 정상적으로 설정되어 읽혀진다는 것을 의미합니다.
이 클래스를 PHP V5로 변환하는 첫 번째 단계는 생성자의 이름을 바꾸는 것입니다. PHP V5에서는 객체(생성자)를 초기화하는 방법을 __construct라고 합니다. 이 작은 변화는 아래와 같습니다.
목록 2. access1.php5
<?php
클래스 구성
{
var $_items = array();
함수 __construct() {
$this->_items[ 'imgpath' ] = '이미지';
}
함수 get( $key ) {
$this->_items[ $key ]를 반환합니다.
}
}
$c = 새로운 구성();
echo( $c->get( 'imgpath' )."n" );
?>
이번 변화는 크지 않습니다. 방금 PHP V5 규칙으로 옮겼습니다. 다음 단계는 클래스 사용자가 $_items 멤버 변수를 직접 읽고 쓸 수 없도록 클래스에 액세스 제어를 추가하는 것입니다. 이 변경 사항은 아래와 같습니다.
목록 3. access2.php5
<?php
클래스 구성
{
비공개 $_items = array();
공개 함수 __construct() {
$this->_items[ 'imgpath' ] = '이미지';
}
공개 함수 get( $key ) {
$this->_items[ $key ]를 반환합니다.
}
}
$c = 새로운 구성();
echo( $c->get( 'imgpath' )."n" );
?>
이 개체의 사용자가 항목 배열에 직접 액세스하는 경우 배열이 비공개로 표시되므로 액세스가 거부됩니다. 다행스럽게도 사용자는 get() 메소드가 매우 환영받는 읽기 권한을 제공한다는 것을 발견했습니다.
보호된 권한을 사용하는 방법을 설명하려면 Configuration 클래스에서 상속해야 하는 또 다른 클래스가 필요합니다. 나는 그 클래스를 DBConfiguration이라고 불렀고 그 클래스가 데이터베이스에서 구성 값을 읽을 것이라고 가정했습니다. 이 설정은 아래와 같습니다.
목록 4. access3.php
<?php
클래스 구성
{
보호된 $_items = array()
공개 함수 __construct() {
$this->로드();
}
보호된 함수 load() { }
공개 함수 get( $key ) {
$this->_items[ $key ]를 반환합니다.
}
}
클래스 DBConfiguration은 구성을 확장합니다.
{
보호된 함수 load() {
$this->_items[ 'imgpath' ] = '이미지';
}
}
$c = 새로운 DBConfiguration();
echo( $c->get( 'imgpath' )."n" );
?>
이 목록은 보호된 키워드의 올바른 사용법을 보여줍니다. 기본 클래스는 load()라는 메서드를 정의합니다. 이 클래스의 서브클래스는 load() 메서드를 재정의하여 항목 테이블에 데이터를 추가합니다. load() 메서드는 클래스 및 해당 하위 클래스 내부에 있으므로 모든 외부 소비자에게 이 메서드가 표시되지는 않습니다. 키워드가 모두 비공개인 경우 load() 메서드를 재정의할 수 없습니다.
이 디자인은 별로 마음에 들지 않지만 DBConfiguration 클래스에 항목 배열에 대한 액세스 권한을 부여해야 했기 때문에 선택했습니다. 항목 배열을 계속해서 Configuration 클래스에 의해 완전히 유지하고 싶습니다. 그러면 다른 하위 클래스가 추가될 때 해당 클래스가 항목 배열을 유지 관리하는 방법을 알 필요가 없습니다. 다음과 같이 변경했습니다.
목록 5. access4.php5
<?php
클래스 구성
{
비공개 $_items = array();
공개 함수 __construct() {
$this->로드();
}
보호된 함수 load() { }
보호된 함수 추가( $key, $value ) {
$this->_items[ $key ] = $value;
}
공개 함수 get( $key ) {
$this->_items[ $key ]를 반환합니다.
}
}
클래스 DBConfiguration은 구성을 확장합니다.
{
보호된 함수 load() {
$this->add( 'imgpath', 'images' );
}
}
$c = 새로운 DBConfiguration();
echo( $c->get( 'imgpath' )."n" );
?>
이제 하위 클래스가 보호된 add() 메서드를 사용하여 목록에 구성 항목을 추가하므로 항목 배열을 비공개로 설정할 수 있습니다. Configuration 클래스는 하위 클래스에 관계없이 구성 항목을 저장하고 읽는 방식을 변경할 수 있습니다. load() 및 add() 메서드가 동일한 방식으로 실행되는 한 하위 클래스화에는 문제가 없습니다.
나에게는 추가된 액세스 제어가 PHP V5로의 전환을 고려하는 주된 이유입니다. Grady Booch가 PHP V5가 4대 객체지향 언어 중 하나라고 말했기 때문일까요? 아니요, 모든 메소드와 멤버가 공개로 정의된 100KLOC C++ 코드를 유지하는 작업을 수락한 적이 있기 때문입니다. 이러한 정의를 정리하는 데 3일이 걸렸으며 그 과정에서 오류 수가 크게 줄어들고 유지 관리성이 향상되었습니다. 왜? 접근 제어가 없으면 객체가 다른 객체를 어떻게 사용하는지 알 수 없고, 어떤 장애물을 극복해야 하는지 알지 못하면 어떤 변경도 불가능하기 때문입니다. C++에서는 최소한 컴파일러를 사용할 수 있습니다. PHP에는 컴파일러가 제공되지 않으므로 이러한 유형의 액세스 제어가 더욱 중요해집니다.
계약 프로그래밍
PHP V4에서 PHP V5로 마이그레이션할 때 활용해야 할 다음 중요한 기능은 인터페이스, 추상 클래스 및 메서드를 통한 계약 프로그래밍 지원입니다. 목록 6은 PHP V4 코더가 인터페이스 키워드를 전혀 사용하지 않고 기본 인터페이스를 빌드하려고 시도한 Configuration 클래스 버전을 보여줍니다.
목록 6. 인터페이스.php4
<?php
클래스 I구성
{
함수 get( $key ) { }
}
클래스 구성은 IConfiguration을 확장합니다.
{
var $_items = array();
함수 구성() {
$this->로드();
}
함수 로드() { }
함수 get( $key ) {
$this->_items[ $key ]를 반환합니다.
}
}
클래스 DBConfiguration은 구성을 확장합니다.
{
함수 로드() {
$this->_items[ 'imgpath' ] = '이미지';
}
}
$c = 새로운 DBConfiguration();
echo( $c->get( 'imgpath' )."n" );
?>
목록은 Configuration 클래스 또는 파생 클래스에서 제공하는 모든 인터페이스를 정의하는 작은 IConfiguration 클래스로 시작됩니다. 이 인터페이스는 클래스와 모든 사용자 간의 계약을 정의합니다. 계약에는 IConfiguration을 구현하는 모든 클래스에 get() 메서드가 있어야 하며 IConfiguration의 모든 사용자는 get() 메서드만 사용해야 한다고 명시되어 있습니다.
아래 코드는 PHP V5에서 실행되지만 아래와 같이 제공되는 인터페이스 시스템을 사용하는 것이 더 좋습니다.
목록 7. 인터페이스1.php5
<?php
인터페이스 IConfiguration
{
함수 get($key);
}
클래스 구성은 IConfiguration을 구현합니다.
{
...
}
클래스 DBConfiguration은 구성을 확장합니다.
{
...
}
$c = 새로운 DBConfiguration();
echo( $c->get( 'imgpath' )."n" );
?>
한편으로는 독자가 실행 상태를 더 명확하게 이해할 수 있는 반면, 단일 클래스는 여러 인터페이스를 구현할 수 있습니다. Listing 8에서는 PHP의 내부 인터페이스인 Iterator 인터페이스를 구현하기 위해 Configuration 클래스를 확장하는 방법을 보여줍니다.
목록 8. 인터페이스2.php5
<?php
인터페이스 IConfiguration {
...
}
클래스 구성은 IConfiguration, Iterator를 구현합니다.
{
비공개 $_items = array();
공개 함수 __construct() {
$this->로드();
}
보호된 함수 load() { }
보호된 함수 추가( $key, $value ) {
$this->_items[ $key ] = $value;
}
공개 함수 get( $key ) {
$this->_items[ $key ]를 반환합니다.
}
공개 함수 rewind() { 재설정($this->_items) }
공용 함수 current() { return current($this->_items) }
공개 함수 키() { return key($this->_items) }
공개 함수 next() { return next($this->_items) }
공개 함수 valid() { return ( $this->current() !== false );
}
클래스 DBConfiguration은 구성을 확장합니다.
...
}
$c = 새로운 DBConfiguration();
foreach( $c as $k => $v ) { echo( $k." = ".$v."n" );
?>
Iterator 인터페이스를 사용하면 모든 클래스가 소비자의 배열로 표시될 수 있습니다. 스크립트 끝에서 볼 수 있듯이 foreach 연산자를 사용하여 Configuration 개체의 모든 구성 항목을 반복할 수 있습니다. PHP V4에는 이 기능이 없지만 애플리케이션 내에서 다양한 방법으로 이 기능을 사용할 수 있습니다.
인터페이스 메커니즘의 장점은 메서드를 구현하지 않고도 계약을 신속하게 통합할 수 있다는 것입니다. 마지막 단계는 지정된 모든 메서드를 구현해야 하는 인터페이스를 구현하는 것입니다. PHP V5의 또 다른 유용한 새 기능은 추상 클래스입니다. 이를 통해 기본 클래스로 인터페이스의 핵심 부분을 쉽게 구현한 다음 해당 인터페이스를 사용하여 엔터티 클래스를 만들 수 있습니다.
추상 클래스의 또 다른 용도는 기본 클래스가 인스턴스화되지 않는 여러 파생 클래스에 대한 기본 클래스를 만드는 것입니다. 예를 들어 DBConfiguration과 Configuration이 동시에 존재하는 경우에는 DBConfiguration만 사용할 수 있습니다. Configuration 클래스는 단지 기본 클래스, 즉 추상 클래스입니다. 따라서 아래와 같이 abstract 키워드를 사용하여 이 동작을 강제할 수 있습니다.
목록 9. abstract.php5
<?php
추상 클래스 구성
{
보호된 $_items = array()
공개 함수 __construct() {
$this->로드();
}
추상 보호 함수 load();
공개 함수 get( $key ) {
$this->_items[ $key ]를 반환합니다.
}
}
클래스 DBConfiguration은 구성을 확장합니다.
{
보호된 함수 load() {
$this->_items[ 'imgpath' ] = '이미지';
}
}
$c = 새로운 DBConfiguration();
echo( $c->get( 'imgpath' )."n" );
?>
이제 구성 유형의 개체를 인스턴스화하려는 모든 시도는 시스템에서 클래스를 추상적이고 불완전한 것으로 간주하므로 오류가 발생합니다.
정적 메소드 및 멤버
PHP V5의 또 다른 중요한 새 기능은 클래스의 정적 멤버 및 메소드에 대한 지원입니다. 이 기능을 사용하면 널리 사용되는 싱글톤 패턴을 사용할 수 있습니다. 애플리케이션에는 구성 개체가 하나만 있어야 하므로 이 패턴은 Configuration 클래스에 이상적입니다.
목록 10에서는 싱글톤으로 구성된 Configuration 클래스의 PHP V5 버전을 보여줍니다.
목록 10. static.php5
<?php
클래스 구성
{
개인 $_items = 배열();
정적 개인 $_instance = null;
정적 공개 함수 get() {
if ( self::$_instance == null )
self::$_instance = 새로운 구성();
self::$_instance를 반환합니다.
}
개인 함수 __construct() {
$this->_items[ 'imgpath' ] = '이미지';
}
공개 함수 __get( $key ) {
$this->_items[ $key ]를 반환합니다.
}
}
echo( 구성::get()->{ 'imgpath' }."n" );
?>
static 키워드는 다양한 용도로 사용됩니다. 단일 유형의 모든 개체에 대한 일부 전역 데이터에 액세스해야 하는 경우 이 키워드를 사용하는 것이 좋습니다.
Magic Method
PHP V5의 또 다른 큰 새 기능은 객체가 객체의 인터페이스를 빠르게 변경할 수 있도록 하는 매직 메소드 지원입니다. 예를 들어 Configuration 객체의 각 구성 항목에 대한 멤버 변수를 추가합니다. get() 메서드를 사용할 필요가 없습니다. 아래와 같이 특정 항목을 찾아서 배열로 처리하기만 하면 됩니다.
목록 11. Magic.php5
<?php
클래스 구성
{
개인 $_items = array();
함수 __construct() {
$this->_items[ 'imgpath' ] = '이미지';
}
함수 __get( $key ) {
$this->_items[ $key ]를 반환합니다.
}
}
$c = 새로운 구성();
echo( $c->{ 'imgpath' }."n" );
?>
이 예에서는 사용자가 객체에서 멤버 변수를 찾을 때마다 호출되는 새로운 __get() 메서드를 만들었습니다. 그런 다음 메서드 내의 코드는 항목 배열을 사용하여 값을 찾고 해당 키워드에 대해 특별히 멤버 변수가 있는 것처럼 해당 값을 반환합니다. 객체가 배열이라고 가정하면 스크립트 끝에서 Configuration 객체를 사용하는 것이 imgpath 값을 찾는 것만큼 간단하다는 것을 알 수 있습니다.
PHP V4에서 PHP V5로 마이그레이션할 때 PHP V4에서는 전혀 사용할 수 없는 이러한 언어 기능을 알고 있어야 하며 클래스의 유효성을 다시 검사하여 해당 기능을 사용할 수 있는 방법을 확인해야 합니다.
예외는
PHP V5의 새로운 예외 메커니즘을 도입함으로써 이 기사를 마무리합니다. 예외는 오류 처리에 대해 완전히 새로운 사고 방식을 제공합니다. 모든 프로그램은 필연적으로 오류(파일을 찾을 수 없음, 메모리 부족 등)를 생성합니다. 예외가 사용되지 않으면 오류 코드가 반환되어야 합니다. 아래 PHP V4 코드를 살펴보세요.
목록 12. file.php4
<?php
함수 파싱라인( $l )
{
// ...
반환 배열( '오류' => 0,
data => array() // 여기에 데이터가 있습니다.
);
}
함수 readConfig( $path )
{
if ( $path == null ) return -1;
$fh = fopen( $path, 'r' );
if ( $fh == null ) return -2
while( !feof( $fh ) ) {
$l = fgets( $fh );
$ec = 파싱라인( $l );
if ( $ec['error'] != 0 ) return $ec['error'];
}
fclose( $fh );
0을 반환합니다.
}
$e = readConfig( 'myconfig.txt' );
if ( $e != 0 )
echo( "오류가 발생했습니다(".$e.")n" );
?>
이 표준 파일 I/O 코드는 파일을 읽고, 일부 데이터를 검색하고, 오류가 발생하면 오류 코드를 반환합니다. 이 스크립트에 대해 두 가지 질문이 있습니다. 첫 번째는 오류 코드입니다. 이 오류 코드는 무엇을 의미합니까? 이러한 오류 코드의 의미를 알아내려면 이러한 오류 코드를 의미 있는 문자열로 매핑하는 다른 시스템을 만들어야 합니다. 두 번째 문제는parseLine의 반환 결과가 매우 복잡하다는 것입니다. 데이터를 반환하는 데만 필요한데 실제로는 오류 코드와 데이터를 반환해야 합니다. 대부분의 엔지니어(나 자신 포함)는 오류를 관리하기 어렵기 때문에 게을러서 데이터를 반환하고 오류를 무시하는 경우가 많습니다.
목록 13에서는 예외를 사용할 때 코드가 얼마나 명확한지 보여줍니다.
목록 13. file.php5
<?php
함수 파싱라인( $l )
{
// 유효하지 않은 경우 구문 분석하고 던지며 예외를 발생시킵니다.
반환 배열(); // 데이터
}
함수 readConfig( $path )
{
if ( $path == null )
새로운 예외 발생( '잘못된 인수' );
$fh = fopen( $path, 'r' );
if ( $fh == null )
throw new Exception( '파일을 열 수 없습니다.' )
while( !feof( $fh ) ) {
$l = fgets( $fh );
$ec = 파싱라인( $l );
}
fclose( $fh );
}
노력하다 {
readConfig( 'myconfig.txt' );
} catch(예외 $e) {
에코( $e );
}
?>
예외에는 오류에 대한 설명 텍스트가 포함되어 있으므로 오류 코드에 대해 걱정할 필요가 없습니다. 또한 함수는 오류가 발생하면 오류를 발생시키기 때문에 ParseLine에서 반환된 오류 코드를 추적하는 방법에 대해 생각할 필요도 없습니다. 스택은 스크립트 하단에 있는 가장 가까운 try/catch 블록으로 확장됩니다.
예외는 코드 작성 방식에 혁명을 가져올 것입니다. 오류 코드와 매핑으로 인해 골치 아픈 일을 관리하는 대신 처리하려는 오류에 집중할 수 있습니다. 이러한 코드는 읽고 유지하기가 더 쉽고 일반적으로 이익이 되기 때문에 오류 처리를 추가하는 것이 좋습니다.
결론
새로운 객체 지향 기능과 예외 처리 추가는 코드를 PHP V4에서 PHP V5로 마이그레이션해야 하는 강력한 이유를 제공합니다. 보시다시피 업그레이드 과정은 어렵지 않습니다. PHP V5로 확장된 구문은 PHP와 같습니다. 예, 이러한 구문은 Ruby와 같은 언어에서 유래했지만 서로 매우 잘 작동한다고 생각합니다. 그리고 이러한 언어는 소규모 사이트를 위한 스크립팅 언어에서 엔터프라이즈급 애플리케이션을 완성하는 데 사용할 수 있는 언어로 PHP의 범위를 확장합니다.