기타/웹 2.02016. 5. 5. 00:30

현재... GPX 파일(XML 파일의 일종입니다.)을 해석해서 mySQL DB에 넣기 위해 고민 중에 있었습니다. 


제가 예전에 만들어 둔 프로그램에서는 그냥 javascript - ajax로 읽어 들인 후 사용했었습니다. 원래는 이 부분을 사용하려고 했습니다. 그런데, 이 기능을 이용할 경우, 한 개의 waypoint를 추출해서 배열을 만들고, ajax를 통해 PHP로 넘겨주면, PHP속에서 mySQL에 INSERT 시키는 방법을 사용해야 합니다. 그러면 당연히 터무니 없이 속도가 떨어질 것으로 예상됩니다.


그러던 중, W3C PHP 투토리얼 사이트에서 XML Parser에 관한 내용을 보게 되었습니다. 이걸 이용하면 그냥 PHP에서 XML(GPX파일)을 Parsing 한 후, 바로 MySQL로 넣어줄 수 있으니, 훨씬 작업이 간편할 것이라 생각했습니다.


PHP XML Parser는 두 가지 종류가 있습니다. 하나는 SimpleXML Parser이고, 다른 하나는 XML expat 입니다. SimpleXML은 말 그대로 아주 간단합니다. 그냥 파일이나 string을 지정하면, 전체를 구조체로 읽어 들이는 방식입니다. 그 다음엔 $xml->book[0]->title 과 같이 간단한 참조 방식으로 사용하면 됩니다. 


이에 비해 expat는 상당히 복잡합니다. expat는 이벤트 방식의 Parser라고 하고요, 예를 들어 아래와 같은 문장이 있다면...

<from>Jani</from>

이것을 다음과 같은 3개의 이벤트(사실은 4개의 이벤트)로 보내줍니다. 이런 방식으로 파일 전체가 이벤트로 처리되는 겁니다.

    • Start element: from
    • Start CDATA section, value: Jani
    • Close element: from

언뜻 보기에는 많이 복잡해 보였습니다. 그래서 포기할까 생각했습니다. 그런데, 파일의 크기가 클 수록 이 방식이 빠르고 편하다는 설명을 보고 시도해 보기로 했습니다. (simpleXML Parser의 경우 한꺼번에 메모리로 읽어 들이는 방식이라서 시스템에 따라서 문제가 발생할 수도 있답니다.)


expat Parser는 4개의 함수만 사용하면 거의 80%의 문제는 해결할 수 있다고 합니다. 제가 설명할 것도 바로 이 4가지 함수입니다. (일단 예제는 W3C의 예제를 이용하겠습니다.)


    • $parser=xml_parser_create(); // parser를 생성합니다.
    • xml_set_element_handler($parser,"start","stop"); // 생성한 Parser에 Start event handler 와 Stop event handler를 등록. 당연히 "start"/"stop" 은 함수명입니다.
    • xml_set_character_data_handler($parser,"char"); // Parser에 Data event handler를 등록합니다. "char"가 그 함수고요, 물론 이름은 다른 이름을 사용해도 됩니다.
    • xml_parse($parser,$data,feof($fp)); // parser를 실행합니다.

아래는 W3C 사이트에 있는 예제입니다. 빨간 표시를 한게 위에서 설명한 4개의 함수입니다.

xml_parser_create() 아래로 있는 start(), stop(), char()가 바로 event 핸들러입니다. 이 함수들 3개를 적당히 이용해서 데이터를 처리해주면 됩니다.


<?php
// Initialize the XML parser
$parser=xml_parser_create();

// Function to use at the start of an element
function start($parser,$element_name,$element_attrs) {
  switch($element_name) {
    case "NOTE":
    echo "-- Note --<br>";
    break;
    case "TO":
    echo "To: ";
    break;
    case "FROM":
    echo "From: ";
    break;
    case "HEADING":
    echo "Heading: ";
    break;
    case "BODY":
    echo "Message: ";
  }
}

// Function to use at the end of an element
function stop($parser,$element_name) {
  echo "<br>";
}

// Function to use when finding character data
function char($parser,$data) {
  echo $data;
}

// Specify element handler
xml_set_element_handler($parser,"start","stop");

// Specify data handler
xml_set_character_data_handler($parser,"char");

// Open XML file
$fp=fopen("note.xml","r");

// Read data
while ($data=fread($fp,4096)) {
  xml_parse($parser,$data,feof($fp)) or 
  die (sprintf("XML Error: %s at line %d", 
  xml_error_string(xml_get_error_code($parser)),
  xml_get_current_line_number($parser)));
}

// Free the XML parser
xml_parser_free($parser);
?>


이 파일을 사용해서 note.xml을 parsing하는 순서를 보여드리면...


<?xml version="1.0" encoding="UTF-8"?>

<note>

<to>Tove</to>

<from>Jani</from>

<heading>Reminder</heading>

<body>Don't forget me this weekend!</body>

</note>


먼저 첫줄 <?xml... 은 그냥 무시됩니다. 

두번째 줄 <note>가 들어오면, start event handler로 등록된 start() 함수가 호출되는데, 이때 $element_name에는 "NOTE"가 지정됩니다. 따라서 case 문 등을 사용해 이를 처리해 줍니다.


function start($parser,$element_name,$element_attrs) {

  switch($element_name) {

    case "NOTE":

    echo "-- Note --<br>";

    break;

    case "TO":

    ....


그 다음엔 data event handler가 호출됩니다. (<NOTE>의 경우에는 data에 공백 "  " 만 전달됩니다.)


function char($parser,$data) {

  echo $data;

}


그 다음엔 <to>Tove</to>가 처리될 차례죠. 이것도 비슷한 방식으로 아래와 같은 순서로 처리됩니다.


TO ->start event

Tove -> data event

TO ->end event

" " -> data event


이런 식으로 계속 이벤트를 처리하는 방식으로 처리하면 됩니다. 하나의 노드가 여러개의 이벤트로 나눠오기 때문에 데이터를 처리하는 게 복잡합니다만, 그래도 가능합니다.


====

아래는 제가 테스트해본 프로그램입니다. W3C의 books 예제파일 을 읽어들여서 이를 mySQL 에 삽입하는 예제입니다. 


여기에서 약간 중요한 점이 두 가지 있습니다. 위의 예제와는 달리, xml에 attribute가 있어서 이를 처리하는 부분이 추가되었는데요, 여기에서 each() 함수를 사용했습니다. 또 다른 하나는, mySQL에 입력할 때, prepared statement를 사용했습니다. $stmt=$conn->prepare()를 사용하여 미리 구문만 mySQL로 보내어 미리 최적화를 시킨 후, $stmt->execute();로 실행시켰습니다. 이렇게 해야 실행속도가 빨라진 답니다.



나머지는 아래의 코멘트를 참고하세요.


민, 푸른하늘


<?php


// mySQL DB 연결

require_once 'mySQL_login.php'; //이 파일은 각자의 환경에 맞게 준비해야 함

$conn = new mysqli($db_hostname,$db_username,$db_password, $db_database);

if($conn->connect_errno) {

    die("Connection failed: " . $connection->connect_error);

}


// Table이 미리 있다면 삭제함


$sql = "DROP TABLE IF EXISTS books";

if (!$conn->query($sql)) {

die("Error droping table: " . $conn->error);

}


// Table 생성

$sql = "CREATE TABLE `books` (

  `title` varchar(50) NOT NULL,

  `category` varchar(30) NOT NULL,

  `author` varchar(30) NOT NULL,

  `lang` varchar(10) NOT NULL,

  `year` smallint(6) NOT NULL,

  `price` float NOT NULL

) ENGINE=MyISAM";


if (!$conn->query($sql)) {

die("Error creating table: " . $conn->error);

}


// bind_para 에 사용되는 변수 초기화

$b_title = $b_author = $b_category = $b_lang = "";

$b_year = $b_price = 0;


// prepared statement 준비

$stmt = $conn->prepare("INSERT INTO books (title, author, category, lang, year, price) VALUES (?, ?, ?, ?, ?, ?)");

$stmt->bind_param("ssssid", $b_title, $b_author, $b_category, $b_lang, $b_year, $b_price);


// XML parser 생성

$parser=xml_parser_create();


// XML을 parsing 한 후 저장할 array 초기화

$num_books = 0;

$book = array("TITLE"=>"", "AUTHOR"=>"", "CATEGORY" => "" , "LANG" => "", "YEAR" => 0, "PRICE" => 0);

$index = "";


// Element Start event handler. 여기에서는 $index만 저장함.

function start($parser,$element_name,$element_attrs) {


    global $index,$book;

    

    switch($element_name) {

      case "BOOK":

          break;

      case "TITLE":

          $index = "TITLE";

          break;

      case "AUTHOR":

          $index = "AUTHOR";

          break;

      case "YEAR":

          $index = "YEAR";

          break;

      case "PRICE":

          $index = "PRICE";

          break;

    }


    // attribute의 경우엔 each로 처리함. 

    if(!empty($element_attrs)){

        $temp = each($element_attrs);

        $book[$temp[0]] = $temp[1];

    }

}


// Element Stop event handler. BOOK이 끝나는 시점에서 $stmt를 실행하여 $book array에 들어 있는 내용을 mySQL로 저장. 

function stop($parser,$element_name) {

    

    global $num_books, $book, $index, $stmt;

    global $b_title, $b_author, $b_category,$b_lang, $b_year, $b_price;

    $b_title = $book['TITLE'];

    $b_author = $book['AUTHOR'];

    $b_category = $book['CATEGORY'];

    $b_lang=$book['LANG'];

    $b_year = $book['YEAR'];

    $b_price = $book['PRICE'];

    

    if($element_name == "BOOK") {

          $num_books++;

          $stmt->execute();

          print_r($book);

          echo "<br>";

    }

}


// Element Data event handler. 실제의 값을 $book 배열에 저장.

function char($parser,$data) {

  global $book, $index;

  

  if($index != "") {

    $book[$index] = $data;

    $index = "";

  }

}


// Specify element handler

xml_set_element_handler($parser,"start","stop");


// Specify data handler

xml_set_character_data_handler($parser,"char");


// Open XML file

$fp=fopen("books.xml","r");


// Read data. feof()을 만날 때까지 계속 파일을 읽어들이고 parsing 함.

while ($data=fread($fp,4096)) {

  xml_parse($parser,$data,feof($fp)) or 

    die (sprintf("XML Error: %s at line %d", 

  xml_error_string(xml_get_error_code($parser)), xml_get_current_line_number($parser)));

}


// Free the XML parser

xml_parser_free($parser);

?>




Posted by 푸른하늘 푸른하늘이

댓글을 달아 주세요

기타/웹 2.02016. 4. 26. 20:04

예전에 반쯤 공부하다가 팽겨쳐 뒀던 PHP를 다시 공부하기로 했습니다. 


먼저 예전처럼 Zend Server를 설치하기로 했습니다. 제가 공부하는 책에서 Zend Server를 설치하여 사용하기 때문이었습니다.


그런데 그동안 업그레이드가 되면서 무료버전이 사라졌더군요. Free Trial 버전은 1개월, 기타 학생용버전 등은 1년 이후에는 정식버전을 사야하는 것 같았습니다. 뭐 그래서 과감하게 포기. 설치하다가 에러가 발생하는 게 아니었으면 계속 갔을지도 모르지만...


하여튼... 그래서 best WAMP로 검색을 좀 해봤습니다. WAMP는 Windows + Apache + MySQL + PHP 로 구성된 Suite를 말합니다. 물론 Zend Server를 순위에 올린 사이트도 있었지만, 다른 제품도 많이 있더군요. 저는 그중에서 easyPHP가 제일 마음에 들었습니다. 쉽다니까. ㅎㅎ


easyPHP.org 에 접속하면 아래와 같은 화면이 보입니다. 좌측은 DevServer 즉, 개발용 서버이고, 우측은 WebServer, 즉 단순 서버용입니다. 저는 최신버전의 DevServer를 택했습니다.



버튼을 클릭하면 설치프로그램이 다운로드 되고, 그것을 실행시키면 다음과 같은 화면이 나옵니다. 아무런 설정이 필요 없습니다. 그냥 자동으로 설치됩니다.



그런데, 설치가 끝난 후 실행시키려면 다음과 같은 화면이 뜹니다. 뭔가 설치를 해야 한다고 나오네요.



원래 첫화면에도 "Visual C++ Redistributable for Visual Studio 2015 x86 or x64 required" 라고 되어 있는데, 자신의 컴퓨터에 맞게 x86 버전이나 x64 버전을 설치하면 됩니다. 



그런데... 이 두개의 파일을 모두 설치했음에도 동일한 에러가 발생하네요. 그래서 좀 더 찾아보니, "Visual C++ Redistributable for Visual Studio 2012"도 설치해야 한답니다. 그래서... ㅠㅠ 어쨌든 이렇게 다 설치를 했더니 에러가 발생하지 않았습니다. 


자... 이제 시작.!!! 바탕화면에 깔린 아이콘을 클릭!!! 그런데 아무런 변화가 없었습니다. 잠시 기다려도 감감 무소식. 무슨 화면같은게 뜨던가, 웹브라우저에 뭔가 나타나던가... 그래야 할텐데 아무것도 나타나지 않았습니다. 또한 홈페이지에는 당연히 뭔가 문서가 있겠지 싶었는데, 아무것도 안보이니 정말 황당했습니다. 


이때 아래와 같이 윈도 오른쪽 아래에 있는 '숨겨진 아이콘'에서 DevServer 아이콘을 클릭하고 Open Dashboard를 선택하면...



웹브라우저에 아래와 같은 화면이 나타납니다. 저는 여기에서 아래처럼 3가지를 먼저 설정했습니다. phpMyAdmin은 아직 제가 몰라서 나중으로...


여기에서 HTTP SERVER를 누르고 APACHE 서버를 선택해서 Start를 눌러주고요,



DB SERVER에서 MYSQL을 선택하고 Start를 눌러주면 됩니다. 그러고 나서 다시 Dashboard로 돌아오면 아래와 같은 모습이 됩니다.



이제 마지막으로 작업공간을 지정하면 됩니다. 윗쪽은 easyPHP에서 사용하는 디렉토리명이고, 아래는 실제 사용할 폴더명을 복사해서 넣어주면 됩니다. 



====

이 정도하면, 개발용 설정은 거의(아주 간략하게만) 끝났습니다. 하나만 실험을 해보죠.

먼저 아래와 같은 내용으로 hello.php 라는 파일을 만들어 작업 디렉토리에 넣어줍니다.


<?php 

echo "Hello World!"; 

?>


이제 작업 디렉토리를 확장시켜보면 아래와 같이 hello.php 가 들어있습니다.


hello.php 오른쪽에 있는 눈모양의 그림을 클릭하면, 새로운 창이 열리고 실행됩니다.


===

2016/5/14 추가


그동안 easyPHP를 계속 사용해 왔지만, 에러가 계속 발생하여 여러번 재설치하다가 오늘 드디어 XAMPP를 설치했습니다. (참고로 easyPHP는 별도로 삭제 프로그램이 없으며, 그냥 설치된 디렉토리를 찾아 지워주기만 하면 삭제가 완료됩니다.)


XAMPP도 설치는 그다지 까다로울 것이 없습니다. 그냥 AphacheFriends 사이트에서 원하는 파일을 다운로드 받아 실행만 시키면 됩니다. 저는 PHP the right way의 권고에 따라 PHP7 버전으로 설치했습니다. Backward compatibility의 문제가 거의 없고, 속도도 빠르다고 하니까요.


다만 설치 초기에 UAC 설정으로 인해 잘 돌아가지 않을 수 있다... 는 경고가 뜨는데, Program Files(x86)에 설치하지 말고 별도의 폴더 (예: c:\xampp)에 설치해주면 문제가 없다고 합니다.



그 다음에는 easyPHP와 그다지 다를 건 없었습니다. xamppcontrol.exe을 실행시켜 보면 아래와 같은 창이 뜨는데, 여기에서 Apache와 MySQL (실제로는 MariaDB)를 실행시킬 수 있고, 설정도 바꿀 수 있습니다. Port 번호만 유의하면 문제가 없습니다.



또한 XDebug 설정도 EasyPHP의 경우와 거의 동일합니다. 자신의 버전에 맞는 XDebug 파일을 복사해 두고, php.ini 파일을 적당하게 수정만 해주면 완료입니다. 


머... 이상으로 대충~~


민, 푸른하늘

Posted by 푸른하늘 푸른하늘이

댓글을 달아 주세요

기타/웹 2.02014. 6. 24. 17:06

이제 4번째네요... PHP로 할 수 있는 게 정말 다양하다는 걸 다시한번 느꼈습니다. 그리고... 그냥 javascript면 대충 해결되겠지... 라고 생각했는데... 참으로 거시기하네요. 오히려 웹앱을 개발하려면 서버측 개발이 더 중요한데 말이죠.


MySQL  개요


  • MySQL은 웹에서 사용되는 DBMS. 서버에서 수행됨. 크기에 관계없이 사용가능. 
  • 빠르고, 신뢰성 높고, 사용하기 쉬움. 표준 SQL의 지원, 다양한 platform 지원, 무료
  • Oracle에서 개발 보급. 이름은 공동개발자의 딸의 이름(My)를 따서 지음
  • 특히 MySQL + PHP 를 하면 cross-platform 으로 매우 유용함
  • MySQL을 어떻게 사용하는가는 SQL을 얼마나 잘 할 수 있는가에 달려있음.


MySQL 서버에 접속


<?php

$con = mysqli_connect("example.com", "peter", "abc123", "my_db");

// host, username, password, db_name

if (mysqli_connect_errno()) {

echo "Failed to connect to MySQL. : " . mysqli_connect_errno();

}


....


mysqli_close($con);

?>



Create DB / Create Table


  • cafe24 호스팅의 경우, 새로운 DB를 생성할 수 없음. 기존의 DB를 사용해야 함. 
  • 기존 DB 명은 사용자id와 동일


....

$sql = "CREATE DATABASE my_db";

if(!mysqli_query($con, $sql) {

echo "Error creating DB : " . mysqli_error($con);

} else {

echo "Successful";

}


$sql ="CREATE TABLE Persons(

PID INT NOT NULL AUTO_INCREMENT,     //

PRIMARY KEY(PID),                                // PID를 primary key!

FirstName CHAR(30), 

LastName CHAR(30), 

Age INT)";


if(!mysqli_query($con, $sql) {

echo "Error creating Table : " . mysqli_error($con);

} else {

echo "Successful";

}



Insert a row


  • INSERT INTO table_name VALUES (value1, value2, value3,...)
  • INSERT INTO table_name (column1, column2, column3,...) VALUES (value1, value2, value3,...)

  • 데이터 입력을 받는 폼

<html>

<body>


<form action="insert_data.php" method="post">

Firstname: <input type="text" name="firstname"><br />

Lastname: <input type="text" name="lastname"><br />

Age: <input type="text" name="age"><br />

<input type="submit">

</form>


</body>

</html>


  • 실제로 Table에 입력해 주는 php

<?php

$con=mysqli_connect("localhost","id","pw","dbname");

// Check connection

if (mysqli_connect_errno()) {

  echo "Failed to connect to MySQL: " . mysqli_connect_error();

}


// 폼에서 입력된 항목을 SQL INJECTION 을 방지하기 위해 escape 처리

$firstname = mysqli_real_escape_string($con, $_POST['firstname']);

$lastname = mysqli_real_escape_string($con, $_POST['lastname']);

$age = mysqli_real_escape_string($con, $_POST['age']);


$sql="INSERT INTO Persons (FirstName, LastName, Age)

VALUES ('$firstname', '$lastname', '$age')";


if (!mysqli_query($con,$sql)) {

  die('Error: ' . mysqli_error($con));

}

echo "1 record added";


mysqli_close($con);

?>



Read Data from MySQL


  • mysqli_query()를 사용하여 SELECT 문을 사용하면 데이터를 읽을 수 있음
  • mysqli_fetch_array를 사용하면 한 record 씩 배열로 받아짐.
  • 그 결과를 html로 표현함.

<?php

$con=mysqli_connect("localhost","id","pw","db_name");

// Check connection

if (mysqli_connect_errno()) {

  echo "Failed to connect to MySQL: " . mysqli_connect_error();

}


$result = mysqli_query($con,"SELECT * FROM Persons");


echo "<table border='1'>

<tr>

<th>Firstname</th>

<th>Lastname</th>

</tr>";


while($row = mysqli_fetch_array($result)) {

  echo "<tr>";

  echo "<td>" . $row['FirstName'] . "</td>";

  echo "<td>" . $row['LastName'] . "</td>";

  echo "</tr>";

}


echo "</table>";


mysqli_close($con);

?>






















Posted by 푸른하늘 푸른하늘이

댓글을 달아 주세요