1. DBQuery 객체
이제 DBQuery 객체는 단순히 저장 프로시저를 에뮬레이트합니다. 일단 실행되면 저장해야 하는 결과 리소스를 반환하고 결과 집합에 함수를 사용하려는 경우(예: num_rows() 또는 fetch_row() ) ), MySqlDB 개체를 전달해야 합니다. 그렇다면 DBQuery 개체가 MySqlDB 개체(실행된 쿼리의 결과에 대해 작동하도록 설계된)에 의해 구현된 함수를 구현하면 어떤 효과가 있을까요? 이전 예제의 코드를 계속 사용하고 결과 리소스가 이제 DBQuery 개체에 의해 관리된다고 가정해 보겠습니다. DBQuery 클래스의 소스 코드는 목록 1에 표시됩니다.
목록 1. DBQuery 클래스 사용.
'mysql_db.php'가 필요합니다.
require_once 'query.php';
$db = 새로운 MySqlDb;
$db->connect('호스트', '사용자 이름', '패스');
$db->query('content_management_system 사용');
$query = 새로운 DBQuery($db);
$query->prepare('사용자 이름=:1S AND pword=:2S AND 만료_시간<:3I'에서 fname, sname을 선택하세요.');
노력하다 {
if($query->execute("visualad", "앞치마", time()))->num_rows() == 1) {
echo('올바른 자격 증명');
} 또 다른 {
echo('잘못된 자격 증명/세션 만료');
}
} 잡기(QueryException $e) {
echo('쿼리 실행 오류: ' . $e);
}
위 수정된 코드에서 가장 관심을 끄는 부분은 catch문과 Execution문입니다.
· 실행 문은 더 이상 결과 리소스를 반환하지 않으며 이제 DBQuery 개체 자체를 반환합니다.
· DBQuery 개체는 이제 우리가 DB 인터페이스에서 이미 익숙하게 알고 있는 num_rows() 함수를 구현합니다.
· 쿼리 실행이 실패하면 QueryException 유형의 예외가 발생합니다. 문자열로 변환하면 발생한 오류에 대한 세부 정보를 반환합니다.
이렇게 하려면 프록시를 사용해야 합니다. 실제로 DBQuery 개체에서 이미 프록시를 사용하고 있지만 이제 이를 MySqlDB 개체에 긴밀하게 바인딩하기 위해 더 자세히 사용하게 됩니다. DBQuery 개체는 DB 인터페이스를 구현하는 개체로 초기화되었으며, 이미 DB 개체의 query() 메서드를 호출하여 쿼리를 실행하는 멤버 함수 실행을 포함하고 있습니다. DBQuery 개체 자체는 실제로 데이터베이스를 쿼리하지 않으며 이 작업을 DB 개체에 맡깁니다. 이는 프록시이며, 동일하거나 유사한 동작을 구현하는 다른 개체에 메시지를 보내 개체가 특정 동작을 구현할 수 있는 프로세스입니다.
이렇게 하려면 DB 개체의 결과 리소스에 대해 작동하는 모든 함수를 포함하도록 DBQuery 개체를 수정해야 합니다. DB 객체의 해당 함수를 호출하고 그 결과를 반환하려면 쿼리 실행 시 저장된 결과를 사용해야 합니다. 다음 기능이 추가됩니다.
목록 2: 프록시를 사용하여 DBQuery 클래스 확장.
클래스DB쿼리
{
.....
공개 함수 fetch_array()
{
if (!is_resource($this->result)) {
throw new Exception('쿼리가 실행되지 않았습니다.');
}
return $this->db->fetch_array($this->result);
}
공개 함수 fetch_row()
{
if (!is_resource($this->result)) {
throw new Exception('쿼리가 실행되지 않았습니다.');
}
return $this->db->fetch_row($this->result);
}
공개 함수 fetch_assoc()
{
if (!is_resource($this->result)) {
throw new Exception('쿼리가 실행되지 않았습니다.');
}
return $this->db->fetch_assoc($this->result);
}
공개 함수 fetch_object()
{
if (!is_resource($this->result)) {
throw new Exception('쿼리가 실행되지 않았습니다.');
}
return $this->db->fetch_object($this->result);
}
공개 함수 num_rows()
{
if (!is_resource($this->result)) {
throw new Exception('쿼리가 실행되지 않았습니다.');
}
return $this->db->num_rows($this->result);
}
}
각 함수의 구현은 매우 간단합니다. 먼저 쿼리가 실행되었는지 확인한 다음 작업을 DB 개체에 위임하고 쿼리 개체 자체인 것처럼 결과를 반환합니다(기본 데이터베이스 함수라고 함).
2. Type Hinting
프록시가 작동하려면 DBQuery 객체의 $db 변수가 DB 인터페이스를 구현하는 객체의 인스턴스인지 확인해야 합니다. 유형 힌트는 함수 매개변수를 특정 유형의 객체로 강제 변환할 수 있게 해주는 PHP 5의 새로운 기능입니다. PHP 5 이전에는 함수 매개변수가 특정 객체 유형인지 확인하는 유일한 방법은 PHP에서 제공되는 유형 검사 함수(즉, is_a())를 사용하는 것이었습니다. 이제 함수 매개변수 앞에 유형 이름을 붙여 간단히 객체 유형을 캐스팅할 수 있습니다. DB 인터페이스를 구현하는 객체가 객체 생성자로 전달되도록 보장하는 DBQuery 객체의 유형 힌트를 이미 보았습니다.
공개 함수 __construct(DB $db)
{
$this->db = $db;
}
유형 힌트를 사용하면 객체 유형뿐만 아니라 추상 클래스 및 인터페이스도 지정할 수 있습니다.
3. 예외 발생
위 코드에서 포착한 것이 QueryException이라는 예외라는 것을 알아차렸을 것입니다(이 객체는 나중에 구현할 것입니다). 예외는 오류와 유사하지만 더 일반적입니다. 예외를 설명하는 가장 좋은 방법은 Emergency를 사용하는 것입니다. 긴급 상황이 "치명적"이지는 않더라도 반드시 처리되어야 합니다. PHP에서 예외가 발생하면 함수, try..catch 블록 또는 스크립트 자체이든 현재 실행 범위가 빠르게 종료됩니다. 그런 다음 예외는 try..catch 블록에 포착되거나 호출 스택의 맨 위에 도달할 때까지 호출 스택을 순회하여 각 실행 범위를 종료합니다. 이때 치명적인 오류가 생성됩니다.
예외 처리는 PHP 5의 또 다른 새로운 기능입니다. OOP와 함께 사용하면 오류 처리 및 보고를 효과적으로 제어할 수 있습니다. try..catch 블록은 예외 처리를 위한 중요한 메커니즘입니다. 일단 포착되면 예외가 포착되어 처리된 다음 코드 줄부터 스크립트 실행이 계속됩니다.
쿼리가 실패하면 실행 함수를 변경하여 예외를 발생시켜야 합니다. QueryException이라는 사용자 지정 예외 개체가 발생합니다. 오류를 일으킨 DBQuery 개체가 여기에 전달됩니다.
목록 3. 예외가 발생합니다.
/**
*현재 쿼리 실행
*
* 현재 쿼리를 실행합니다. 모든 점을 제공된 인수로 바꿉니다.
* .
*
* @parameters: 혼합 $queryParams,... 쿼리 매개변수
* @return: 리소스 A - 쿼리가 실행되는 리소스를 설명하는 참조입니다.
*/
공용 함수 실행($queryParams = '')
{
//예: SELECT * FROM table WHERE name=:1S AND type=:2I AND level=:3N
$args = func_get_args();
if ($this->stored_procedure) {
/*컴파일 함수를 호출하여 쿼리 가져오기*/
$query = call_user_func_array(array($this, 'compile'), $args);
} 또 다른 {
/*저장 프로시저가 초기화되지 않았으므로 표준 쿼리로 실행됩니다*/
$쿼리 = $queryParams;
}
$result = $this->db->query($query);
if (!$result) {
새로운 QueryException($this)을 던져라;
}
$this->결과 = $결과;
/* 이제 객체 자체를 반환하는 방법에 주목하세요. 이를 통해 이 함수의 반환 결과에서 멤버 함수를 호출할 수 있습니다. */
$this를 반환합니다.
}
4. 상속을 사용하여 사용자 정의 예외 발생
PHP에서는 모든 객체를 예외로 발생시킬 수 있지만 먼저 예외는 PHP의 내장 예외 클래스에서 상속되어야 합니다. 사용자 정의 예외를 생성하여 오류에 대한 기타 정보를 기록하거나, 로그 파일에 항목을 생성하거나, 원하는 작업을 수행할 수 있습니다. 사용자 정의 예외는 다음을 수행합니다.
· 쿼리에 의해 생성된 DB 개체의 오류 메시지를 기록합니다.
· 호출 스택을 검사하여 쿼리 오류가 발생한 코드 줄에 대한 정확한 세부 정보를 제공합니다.
· 오류 메시지와 쿼리 텍스트를 문자열로 변환할 때 표시합니다.
오류 메시지와 쿼리 텍스트를 얻으려면 DBQuery 개체를 몇 가지 변경해야 합니다.
1. 새로운 보호 속성인 compiledQuery를 클래스에 추가해야 합니다.
2. compile() 함수는 쿼리의compileQuery 속성을 쿼리 텍스트로 업데이트합니다.
3. 컴파일된 쿼리 텍스트를 검색하려면 함수를 추가해야 합니다.
4. 함수도 추가해야 합니다. 이는 DBQuery 개체와 연결된 현재 DB 개체를 가져옵니다.
목록 4. 예외 발생.
클래스DB쿼리
{
/**
*compile() 또는execute()를 호출한 후 쿼리의 컴파일된 버전을 저장합니다.*
* @var string $compiledQuery
*/
보호된 $compiledQuery;
/**
* 컴파일된 쿼리를 실행하지 않고 반환합니다.
* @parameters: 혼합 $params,...쿼리 매개변수* @return: 문자열—컴파일된 쿼리*/
공용 함수 컴파일($params='')
{
if (! $this->stored_procedure) {
throw new Exception("저장 프로시저가 초기화되지 않았습니다.");
}
/*매개변수 교체*/
$params = func_get_args(); //함수 매개변수 가져오기 $query = preg_replace("/(?compile_callback($params, 1, "2")', $this->query);
return ($this->compiledQuery = $this->add_strings($query)); //문자열을 쿼리에 다시 넣습니다.}
공개 함수 getDB()
{
$this->db를 반환합니다.
}
공개 함수 getCompiledQuery()
{
$this->컴파일된 쿼리를 반환합니다.
}
}
이제 QueryException 클래스를 구현할 수 있습니다. 실제로 오류를 발생시킨 스크립트의 위치를 찾기 위해 호출 스택을 살펴보는 방법에 주목하세요. 예외를 발생시키는 DBQuery 객체가 DBQuery 객체에서 상속되는 하위 클래스인 경우가 바로 그렇습니다.
목록 5: QueryException 클래스.
/**
*쿼리 예외
*
*쿼리를 실행하려고 할 때 오류가 발생하면 {@link DBQuery} 개체에 의해 오류가 발생합니다.
*/
클래스 QueryException은 예외를 확장합니다.
{
/**
*쿼리 텍스트*
* @var string $QueryText;
*/
보호된 $QueryText;
/**
*데이터베이스의 오류 번호/코드*
* @var 문자열 $ErrorCode
*/
보호된 $ErrorNumber;
/**
*데이터베이스의 오류 메시지*
* @var 문자열 $ErrorMessage
*/
보호된 $ErrorMessage;
/**
*클래스 생성자*
* @Parameter: DBQuery $db, 예외를 발생시키는 쿼리 객체 */
공개 함수 __construct(DBQuery $query)
{
/*콜 스택 가져오기*/
$backtrace = $this->GetTrace();
/*실제로 오류가 발생한 위치로 라인과 파일을 설정*/
if (count($backtrace) > 0) {
$x = 1;
/*쿼리 클래스가 상속된 경우 하위 클래스의 호출을 무시해야 합니다*/
while((!isset($backtrace[$x]['line'])) ||
(isset($backtrace[$x]['class']) && is_subclass_of($backtrace[$x]['class'], 'DBQuery')) ||
(strpos(strtolower(@$backtrace[$x]['function']), 'call_user_func')) !== false ) {
/*줄 번호가 없거나 호출된 함수가 DBQuery 클래스의 하위 클래스인 경우 루프 실행*/
++$x;
/*스택의 맨 아래에 도달하면 첫 번째 호출자를 사용합니다*/
if (($x) >= count($backtrace)) {
$x = 개수($backtrace);
부서지다;
}
}
/*위 루프가 한 번 이상 실행되면 이를 1씩 감소시켜 오류를 일으킨 실제 코드 줄을 찾을 수 있습니다*/
if ($x != 1) {
$x -= 1;
}
/*마지막으로 오류를 일으킨 SQL 문을 반영하는 파일 및 줄 번호를 설정할 수 있습니다*/
$this->line = $backtrace[$x]['line'];
$this->file = $backtrace[$x]['파일'];
}
$this->QueryText = $query->getCompiledQuery();
$this->ErrorNumber = $query->getDB()->errno();
$this->ErrorMessage = $query->getDB()->error();
/*슈퍼클래스의 예외 생성자 호출*/
parent::__construct('쿼리 오류', 0);
}
/**
*쿼리 텍스트 가져오기*
* @return 문자열 쿼리 텍스트 */
공개 함수 GetQueryText()
{
$this->QueryText를 반환합니다.
}
/**
*오류 번호 있음*
* @return 문자열 오류 번호 */
공개 함수 GetErrorNumber()
{
$this->ErrorNumber를 반환합니다.
}
/**
*오류 메시지가 나타남*
* @return 문자열 오류 메시지 */
공개 함수 GetErrorMessage()
{
$this->ErrorMessage를 반환합니다.
}
/**
* 객체가 문자열로 변환될 때 호출됩니다.
* @return 문자열 */
공개 함수 __toString()
{
$output = "{$this->line}nn 줄의 {$this->file}에 쿼리 오류가 있습니다";
$output .= "쿼리: {$this->QueryText}n";
$output .= "오류: {$this->ErrorMessage} ({$this->ErrorNumber})nn"
;
}
}
이제 이 섹션의 시작 부분에서 본 코드가 작동합니다.
5. 결론
이 기사에서는 에이전트가 쿼리와 관련된 DB 인터페이스를 특정 쿼리 결과에 대한 작업에 매핑하는 방법을 살펴보았습니다. DBQuery 개체는 fetch_assoc()과 같은 DB 개체와 동일한 함수를 노출합니다. 그러나 이들은 모두 단일 쿼리에 대해 작동합니다. 또한 사용자 정의 예외를 사용하여 오류가 발생한 시기와 위치에 대한 자세한 정보를 제공하는 방법과 오류 처리를 더 잘 제어할 수 있는 방법을 배웠습니다.