이 문서에서는 구성 가능한 PHP 애플리케이션을 만드는 여러 가지 방법을 보여줍니다. 또한 이 기사에서는 애플리케이션의 이상적인 구성 지점을 살펴보고 너무 구성 가능한 애플리케이션과 너무 폐쇄적인 애플리케이션 간의 균형을 모색합니다.
다른 사람이나 회사에서 PHP 애플리케이션을 사용할 수 있도록 하려면 해당 애플리케이션을 구성할 수 있는지 확인해야 합니다. 최소한 사용자가 데이터베이스 로그인 및 비밀번호를 안전한 방식으로 설정하여 그 안에 있는 자료가 공개되지 않도록 허용하십시오.
이 문서에서는 구성 설정을 저장하고 이러한 설정을 편집하는 몇 가지 기술을 보여줍니다. 또한 이 문서에서는 구성 가능하게 만들어야 하는 요소와 구성 초과 또는 부족의 딜레마에 빠지지 않는 방법에 대한 지침도 제공합니다.
INI 파일을 사용한 구성
PHP에는 구성 파일에 대한 지원이 내장되어 있습니다. 이는 데이터베이스 연결 시간 초과 또는 세션 저장 방법과 같은 상수가 정의되는 php.ini 파일과 같은 초기화 파일(INI) 메커니즘을 통해 달성됩니다. 원하는 경우 이 php.ini 파일에서 애플리케이션 구성을 사용자 정의할 수 있습니다. 설명을 위해 php.ini 파일에 다음 코드 줄을 추가했습니다.
myapptempdir=foo
그런 다음 목록 1에 표시된 대로 이 구성 항목을 읽기 위한 작은 PHP 스크립트를 작성했습니다.
목록 1. ini1.php
<?php
함수 get_template_directory()
{
$v = get_cfg_var( "myapptempdir" );
return ( $v == null ) ? "tempdir" : $v;
}
echo( get_template_directory()."n" );
?>
이 코드를 명령줄에서 실행하면 다음과 같은 결과를 얻습니다.
% php ini1.php
푸
%
훌륭해요. 그런데 표준 INI 함수를 사용하여 myapptempdir 구성 항목의 값을 가져올 수 없는 이유는 무엇입니까? 몇 가지 조사를 한 결과 대부분의 경우 이러한 방법을 사용하면 사용자 정의 구성 항목을 얻을 수 없다는 사실을 발견했습니다. 그러나 get_cfg_var 함수를 사용하여 액세스할 수 있습니다.
이 접근 방식을 더 간단하게 만들려면 아래와 같이 구성 키 이름과 기본값을 매개 변수로 사용하는 두 번째 함수에서 변수에 대한 액세스를 캡슐화합니다.
목록 2. ini2.php
함수 get_ini_value( $n, $dv )
{
$c = get_cfg_var( $n );
반환 ( $c == null ) $dv : $c;
}
함수 get_template_directory()
{
return get_ini_value( "myapptempdir", "tempdir" );
}
이는 INI 파일에 액세스하는 방법에 대한 좋은 개요이므로 다른 메커니즘을 사용하거나 INI 파일을 다른 곳에 저장하려는 경우 많은 기능을 변경하는 수고를 겪을 필요가 없습니다.
두 가지 이유로 애플리케이션 구성에 INI 파일을 사용하지 않는 것이 좋습니다. 첫째, 이렇게 하면 INI 파일을 읽기가 더 쉬워지지만 INI 파일을 안전하게 쓰는 것은 거의 불가능합니다. 따라서 이는 읽기 전용 구성 항목에만 적합합니다. 둘째, php.ini 파일은 서버의 모든 애플리케이션에서 공유되므로 해당 파일에 애플리케이션별 구성 항목을 작성하면 안 된다고 생각합니다.
INI 파일에 대해 무엇을 알아야 합니까? 가장 중요한 것은 아래와 같이 구성 항목을 추가하기 위해 포함 경로를 재설정하는 방법입니다.
목록 3. ini3.php
<?php
echo( ini_get("include_path")."n" );
ini_set("include_path",
ini_get("include_path").":./mylib" );
echo( ini_get("include_path")."n" );
?>
이 예에서는 로컬 mylib 디렉토리를 포함 경로에 추가하여 require 문에 경로를 추가하지 않고도 해당 디렉토리에서 PHP 파일을 요청할 수 있습니다.
PHP의 구성
INI 파일에 구성 항목을 저장하는 일반적인 대안은 간단한 PHP 스크립트를 사용하여 데이터를 유지하는 것입니다. 아래는 예시입니다.
목록 4. config.php
<?php
# 임시 디렉터리의 위치를 지정합니다.
#
$TEMPLATE_DIRECTORY = "임시디렉터리";
?>
이 상수를 사용한 코드는 다음과 같습니다.
목록 5. php.php
<?php
require_once 'config.php';
함수 get_template_directory()
{
전역 $TEMPLATE_DIRECTORY;
$TEMPLATE_DIRECTORY를 반환합니다.
}
echo( get_template_directory()."n" );
?>
코드에는 먼저 구성 파일(config.php)이 포함되어 있으며, 그런 다음 이러한 상수를 직접 사용할 수 있습니다.
이 기술을 사용하면 많은 이점이 있습니다. 첫째, 누군가가 config.php 파일을 탐색하면 페이지가 비어 있습니다. 따라서 config.php를 웹 애플리케이션의 루트와 동일한 파일에 넣을 수 있습니다. 둘째, 모든 편집기에서 편집할 수 있으며 일부 편집기에는 구문 색상 지정 및 구문 확인 기능도 있습니다.
이 기술의 단점은 INI 파일과 같이 읽기 전용 기술이라는 점입니다. 이 파일에서 데이터를 추출하는 것은 매우 쉬운 일이지만 PHP 파일의 데이터를 조정하는 것은 어렵고 어떤 경우에는 불가능할 수도 있습니다.
다음 대안은 읽기 및 쓰기가 모두 가능한 구성 시스템을 작성하는 방법을 보여줍니다.
텍스트 파일
의 이전 두 가지 예는읽기 전용 구성 항목에 적합하지만 읽기 및 쓰기가 모두 가능한 구성 매개변수는 어떻습니까? 먼저 목록 6의 텍스트 구성 파일을 살펴보세요.
목록 6. config.txt
# 내 애플리케이션의 구성 파일
제목=내 앱
TemplateDirectory=tempdir
INI 파일과 파일 형식은 동일하지만 도우미 도구를 직접 작성했습니다. 이를 위해 아래와 같이 나만의 Configuration 클래스를 만들었습니다.
목록 7. text1.php
<?php
클래스 구성
{
개인 $configFile = 'config.txt';
개인 $items = 배열();
함수 __construct() { $this->parse() }
function __get($id) { return $this->items[ $id ] };
함수 파싱()
{
$fh = fopen( $this->configFile, 'r' );
동안( $l = fgets( $fh ) )
{
if ( preg_match( '/^#/', $l ) == false )
{
preg_match( '/^(.*?)=(.*?)$/', $l, $found );
$this->items[ $found[1] ] = $found[2];
}
}
fclose( $fh );
}
}
$c = 새로운 구성();
echo( $c->TemplateDirectory."n" );
?>
이 코드는 먼저 Configuration 객체를 생성합니다. 생성자는 다음으로 config.txt를 읽고 구문 분석된 파일 내용으로 로컬 변수 $items를 설정합니다.
그런 다음 스크립트는 개체에 직접 정의되지 않은 TemplateDirectory를 찾습니다. 따라서 $id가 'TemplateDirectory'로 설정된 매직 __get 메서드가 호출되어 해당 키에 대한 $items 배열의 값을 반환합니다.
이 __get 메서드는 PHP V5 환경에만 해당되므로 이 스크립트는 PHP V5에서 실행되어야 합니다. 실제로 이 기사의 모든 스크립트는 PHP V5에서 실행되어야 합니다.
명령줄에서 이 스크립트를 실행하면 다음과 같은 결과가 표시됩니다:
% php text1.php
임시 직원
%
모든 것이 예상됩니다. 개체는 config.txt 파일을 읽고 TemplateDirectory 구성 항목에 대한 올바른 값을 가져옵니다.
하지만 구성 값을 설정하려면 어떻게 해야 할까요? 이 클래스에서 새 메서드와 일부 새 테스트 코드를 생성하면 아래와 같이 이 기능을 얻을 수 있습니다.
목록 8. text2.php
<?php
클래스 구성
{
...
function __get($id) { return $this->items[ $id ] }
function __set($id,$v) { $this->items[ $id ] = $v }
함수 구문 분석() { ... }
}
$c = 새로운 구성();
echo( $c->TemplateDirectory."n" );
$c->TemplateDirectory = 'foobar';
echo( $c->TemplateDirectory."n" );
?>
이제 __get 함수의 "사촌"인 __set 함수가 있습니다. 이 함수는 멤버 변수의 값을 가져오지 않습니다. 이 함수는 멤버 변수를 설정할 때 호출됩니다. 하단의 테스트 코드는 값을 설정하고 새로운 값을 출력합니다.
명령줄에서 이 코드를 실행하면 다음과 같은 일이 발생합니다:
% php text2.php
임시 직원
푸바
%
매우 좋은! 하지만 변경 사항이 수정되도록 파일에 저장하려면 어떻게 해야 합니까? 이렇게 하려면 파일을 쓰고 읽어야 합니다. 아래와 같이 파일 쓰기를 위한 새로운 기능이 추가되었습니다.
목록 9. text3.php
<?php
클래스 구성
{
...
함수 저장()
{
$nf = '';
$fh = fopen( $this->configFile, 'r' );
동안( $l = fgets( $fh ) )
{
if ( preg_match( '/^#/', $l ) == false )
{
preg_match( '/^(.*?)=(.*?)$/', $l, $found );
$nf .= $found[1]."=".$this->항목[$found[1]]."n";
}
또 다른
{
$nf .= $l;
}
}
fclose( $fh );
copy( $this->configFile, $this->configFile.'.bak' );
$fh = fopen( $this->configFile, 'w' );
fwrite( $fh, $nf );
fclose( $fh );
}
}
$c = 새로운 구성();
echo( $c->TemplateDirectory."n" );
$c->TemplateDirectory = 'foobar';
echo( $c->TemplateDirectory."n" );
$c->저장();
?>
새로운 저장 기능이 config.txt를 교묘하게 조작합니다. 업데이트된 구성 항목으로 파일을 다시 작성하는 대신(주석을 제거함) 파일을 읽고 $items 배열의 내용을 유연하게 다시 작성했습니다. 이렇게 하면 파일의 주석이 보존됩니다.
명령줄에서 스크립트를 실행하고 텍스트 구성 파일의 내용을 출력하면 다음 출력을 볼 수 있습니다.
목록 10. 함수 출력 저장
%php text3.php
임시 직원
푸바
% 고양이 구성.txt
#내 애플리케이션의 구성 파일
제목=내 앱
TemplateDirectory=foobar
%
이제 원본 config.txt 파일이 새 값으로 업데이트됩니다.
XML 구성 파일
텍스트 파일은 읽고 편집하기 쉽지만 XML 파일만큼 널리 사용되지는 않습니다. 또한 마크업, 특수 기호 이스케이프 등을 이해하는 XML용 편집기가 많이 있습니다. 그렇다면 구성 파일의 XML 버전은 어떤 모습일까요? 목록 11에서는 XML 형식의 구성 파일을 보여줍니다.
목록 11. config.xml
<?xml version="1.0"?>
<구성>
<제목>마이앱</제목>
<템플릿디렉토리>tempdir</템플릿디렉토리>
</config>
목록 12에서는 XML을 사용하여 구성 설정을 로드하는 Configuration 클래스의 업데이트된 버전을 보여줍니다.
목록 12. xml1.php
<?php
클래스 구성
{
개인 $configFile = 'config.xml';
개인 $items = 배열();
함수 __construct() { $this->parse() }
function __get($id) { return $this->items[ $id ] };
함수 파싱()
{
$doc = 새로운 DOMDocument();
$doc->로드( $this->configFile );
$cn = $doc->getElementsByTagName( "config" );
$nodes = $cn->item(0)->getElementsByTagName( "*" );
foreach($nodes를 $node로)
$this->items[ $node->nodeName ] = $node->nodeValue;
}
}
$c = 새로운 구성();
echo( $c->TemplateDirectory."n" );
?>
XML에는 또 다른 이점이 있는 것 같습니다. 즉, 코드가 텍스트 버전보다 더 간단하고 쉽습니다. 이 XML을 저장하려면 결과를 텍스트 형식 대신 XML 형식으로 저장하는 다른 버전의 저장 기능이 필요합니다.
목록 13. xml2.php
...
함수 저장()
{
$doc = 새로운 DOMDocument();
$doc->formatOutput = true;
$r = $doc->createElement( "config" );
$doc->appendChild( $r );
foreach( $this-> 항목은 $k => $v )
{
$kn = $doc->createElement( $k );
$kn->appendChild( $doc->createTextNode( $v ) );
$r->appendChild( $kn );
}
copy( $this->configFile, $this->configFile.'.bak' )
$doc->save( $this->configFile );
}
...
이 코드는 새로운 XML DOM(문서 개체 모델)을 생성한 다음 $items 배열의 모든 데이터를 이 모델에 저장합니다. 이 작업을 완료한 후 save 메서드를 사용하여 XML을 파일에 저장합니다.
데이터베이스 사용의
마지막 대안은
데이터베이스를 사용하여 구성 요소의 값을 저장하는 것입니다.첫 번째 단계는 간단한 스키마를 사용하여 구성 데이터를 저장하는 것입니다. 아래는 간단한 패턴입니다.
목록 14. Schema.sql
DROP TABLE IF EXISTS 설정;
CREATE TABLE 설정(
ID MEDIUMINT NOT NULL AUTO_INCREMENT,
이름 텍스트,
값 텍스트,
기본 키(id)
);
이는 애플리케이션 요구 사항에 따라 일부 조정이 필요합니다. 예를 들어 구성 요소를 사용자별로 저장하려면 사용자 ID를 추가 열로 추가해야 합니다.
데이터를 읽고 쓰기 위해 그림 15에 표시된 업데이트된 Configuration 클래스를 작성했습니다.
목록 15. db1.php
<?php
require_once( 'DB.php' );
$dsn = 'mysql://root:password@localhost/config';
$db =& DB::Connect( $dsn, array() );
if (PEAR::isError($db)) { die($db->getMessage())
클래스 구성
{
개인 $configFile = 'config.xml';
개인 $items = 배열();
함수 __construct() { $this->parse() }
function __get($id) { return $this->items[ $id ] };
함수 __set($id,$v)
{
글로벌 $db;
$this->items[ $id ] = $v;
$sth1 = $db->prepare( '이름이=인 곳에서 설정을 삭제하시겠습니까?' );
$db->실행( $sth1, $id );
if (PEAR::isError($db)) { die($db->getMessage()) }
$sth2 = $db->prepare('INSERT INTO 설정 ( id, 이름, 값 ) VALUES ( 0, ?, ? )' );
$db->execute( $sth2, array( $id, $v ) );
if (PEAR::isError($db)) { die($db->getMessage()) }
}
함수 구문 분석()
{
글로벌 $db;
$doc = 새로운 DOMDocument();
$doc->로드( $this->configFile );
$cn = $doc->getElementsByTagName( "config" );
$nodes = $cn->item(0)->getElementsByTagName( "*" );
foreach($nodes를 $node로)
$this->items[ $node->nodeName ] = $node->nodeValue;
$res = $db->query( 'SELECT 이름, 값 FROM 설정' );
if (PEAR::isError($db)) { die($db->getMessage()) }
while( $res->fetchInto( $row ) ) {
$this->items[ $row[0] ] = $row[1];
}
}
}
$c = 새로운 구성();
echo( $c->TemplateDirectory."n" );
$c->TemplateDirectory = '새 foo';
echo( $c->TemplateDirectory."n" );
?>
이것은 실제로 하이브리드 텍스트/데이터베이스 솔루션입니다. 구문 분석 방법을 자세히 살펴보시기 바랍니다. 이 클래스는 먼저 텍스트 파일을 읽어 초기 값을 가져온 다음 데이터베이스를 읽어 키를 최신 값으로 업데이트합니다. 값을 설정한 후에는 키가 데이터베이스에서 제거되고 업데이트된 값으로 새 레코드가 추가됩니다.
이 기사의 여러 버전을 통해 Configuration 클래스가 어떻게 작동하는지 살펴보는 것은 흥미롭습니다. 이 클래스는 동일한 인터페이스를 유지하면서 텍스트 파일, XML 및 데이터베이스에서 데이터를 읽을 수 있습니다. 개발 시에도 동일한 안정성을 갖춘 인터페이스를 사용하는 것이 좋습니다. 이것이 정확히 어떻게 작동하는지 개체 클라이언트에게는 불분명합니다. 핵심은 객체와 클라이언트 간의 계약입니다.
구성이란 무엇이며 구성 방법
너무 많은 구성 옵션과 충분하지 않은 구성 사이에서 올바른 중간 지점을 찾는 것이 어려울 수 있습니다. 확실히 하려면 모든 데이터베이스 구성(예: 데이터베이스 이름, 데이터베이스 사용자 및 비밀번호)을 구성할 수 있어야 합니다. 그 외에도 몇 가지 기본 권장 구성 항목이 있습니다.
고급 설정에서 각 기능에는 별도의 활성화/비활성화 옵션이 있어야 합니다. 애플리케이션에 대한 중요성에 따라 이러한 옵션을 허용하거나 비활성화합니다. 예를 들어 웹 포럼 애플리케이션에서는 지연 기능이 기본적으로 활성화됩니다. 그러나 이메일 알림은 사용자 정의가 필요한 것으로 보이므로 기본적으로 비활성화되어 있습니다.
사용자 인터페이스(UI) 옵션은 모두 한 위치에 설정되어야 합니다. 인터페이스의 구조(예: 메뉴 위치, 추가 메뉴 항목, 인터페이스의 특정 요소에 연결되는 URL, 사용된 로고 등)는 모두 단일 위치로 설정되어야 합니다. 글꼴, 색상 또는 스타일 항목을 구성 항목으로 지정하지 않는 것이 좋습니다. 이는 CSS(Cascading Style Sheets)를 통해 설정되어야 하며 구성 시스템은 사용할 CSS 파일을 지정해야 합니다. CSS는 글꼴, 스타일, 색상 등을 설정하는 효율적이고 유연한 방법입니다. 시중에는 훌륭한 CSS 도구가 많이 있으며, 표준을 직접 설정하려고 하기보다는 응용 프로그램에서 CSS를 잘 활용해야 합니다.
각 기능 내에서 3~10개의 구성 옵션을 설정하는 것이 좋습니다. 이러한 구성 옵션의 이름은 의미 있는 방식으로 지정되어야 합니다. UI를 통해 구성 옵션을 설정할 수 있는 경우 텍스트 파일, XML 파일 및 데이터베이스의 옵션 이름은 인터페이스 요소의 제목과 직접 관련되어야 합니다. 또한 이러한 옵션에는 모두 명확한 기본값이 있어야 합니다.
일반적으로 이메일 주소, 사용할 CSS, 파일에서 참조되는 시스템 리소스 위치, 그래픽 요소의 파일 이름 등의 옵션을 구성할 수 있습니다.
그래픽 요소의 경우 CSS 파일 배치, 그래픽 배치 및 이러한 유형을 포함한 프로필 설정이 포함된 스킨이라는 별도의 프로필 유형을 생성할 수 있습니다. 그런 다음 사용자가 여러 스킨 파일 중에서 선택할 수 있습니다. 이를 통해 애플리케이션의 모양과 느낌을 대규모로 간단하게 변경할 수 있습니다. 이는 또한 사용자에게 다양한 제품 설치 간에 애플리케이션을 스킨할 수 있는 기회를 제공합니다. 이 문서에서는 이러한 스킨 파일을 다루지 않지만 여기에서 배우는 기본 사항을 통해 스킨 파일 지원이 훨씬 간단해집니다.
결론
구성 가능성은 모든 PHP 애플리케이션의 핵심 부분이며 처음부터 설계의 핵심 부분이 되어야 합니다. 이 문서가 구성 아키텍처를 구현하는 데 도움이 되고 허용해야 하는 구성 옵션에 대한 지침을 제공하기를 바랍니다.