뮤직이 정말 좋지 않나요?
작열하는 빛을 받아가며
깨어나는 듯한 하모니입니다.
태양보다 따끈한 뮤직.
코요태 Hollywood
밝은 아리아에 대한 감상뿐만이 아니라
구슬픈 발라드 멜로디에도 충~분히 전해지는
그런 멜로디가 있죠.



바로 이런… 하모니가 아닐…까요?
그럼 곧바로 들어보겠어요!!
정말정말 괜찮은 뮤직입니다.
이 음악은 사람을 즐겁게 합니다. 코요태 Hollywood
들어보는 시간, 큐!,



뮤직을 들으면서 어떤가를 하면
대개 사람들은 집중이 잘 되는거니? 라고 물어본답니다.
잘 되지않는다고 하는 사람들도 많습니다.
그런데 난 노래를 들으면 집중이 잘 되는 편이에요.
기본 두 가지 일을 한다면 더 잘하는 것 같죠.
정신 사납다고 하는데 저는 정말~
그래서~ 오늘 하루도 능률을 위해서라도 멜로디를 틀었죠.


나 혼자서 듣는 뮤직이 못내 아쉬워요.
이처럼 좋은 노래를 모두와 같이 듣지 못하다니
너무 아쉬울 따름이죠. 그쵸?
이렇게 난 저 혼자만의 세계에 빠졌죠.
감정 이입을 하며 들으니 일도 즐거웠답니다.
음악에 맞춰가며 타자도 해보고.
음율에 맟춰서 성실히 일했습니다.
이렇게 순수하게 일을 하다 보고있으면 시간도 정말 잘 갑니다.
가버리는 시간을 잡고 싶을 정도로 말이죠.


흥얼흥얼- 머 이렇게 시간이 빨리빨리 가는 걸까나.
지루하지 않게 노래도 늘 다르고
언제나 섞어서 들으니 듣기 이상하지도 않아요.
이어폰도 정말 좋은 이어폰을 써서 그런걸까요?
이렇게 난 오늘도 노래와 같이 일해요.
멋진 노래가 있다면
늘 다 같이 하고 싶은 마음뿐이죠.
요런 좋은 노래, 저 혼자서 듣기 아주 아깝잖아요?


또 알지 못했던 노래도 많이 발견해놓고 싶네요.
언제, 어디서든 노래와 함께하는 저….
분명히 힘들고 고된 하루일 텐데
음악이 나를 마사지해주는 듯한 시원하고 감사한 감정.
그러한 느낌을 많이 받습니다.
음악을 들으며 좋아지는 하루랍니다.

 

 

============================== PHP 버전이 4점대인 경우 =========================================

 

root로 로그인 한 후

[root@SERVER root]#

/usr/local/php/bin 디렉토리로 이동

[root@SERVER root]# cd /usr/local/php/bin

pear 설치 목록 확인

[root@SERVER bin]# ./pear list

Installed packages, channel pear.php.net:
=========================================
Package              Version  State
Archive_Tar          1.3.2    stable
Console_Getopt       1.2.1    stable
HTML_Template_IT     1.1      stable
Net_UserAgent_Detect 2.0.1    stable
PEAR                 1.5.0    stable
Structures_Graph     1.0.2    stable

 

OLE 설치

[root@SERVER bin]# ./pear install ole-beta
WARNING: channel "pear.php.net" has updated its protocols, use "channel-update pear.php.net" to update
downloading OLE-1.0.0RC1.tgz ...
Starting to download OLE-1.0.0RC1.tgz (12,153 bytes)
.....done: 12,153 bytes
install ok: channel://pear.php.net/OLE-1.0.0RC1

 

Spreadsheet_Excel_Writer 설치

[root@SERVER bin]# ./pear install Spreadsheet_Excel_Writer-beta
WARNING: channel "pear.php.net" has updated its protocols, use "channel-update pear.php.net" to update
downloading Spreadsheet_Excel_Writer-0.9.2.tgz ...
Starting to download Spreadsheet_Excel_Writer-0.9.2.tgz (57,629 bytes)
..............done: 57,629 bytes
install ok: channel://pear.php.net/Spreadsheet_Excel_Writer-0.9.2

 

 

============================== PHP 버전이 5점대인 경우 ==================

* PHP php-5.2.10 에서 pear, pecl이 정상동작을 안한다.

실제로 실행해 보자.

[root@SERVER bin]# pear install ole-beta
pear.php.net is using a unsupported protocal - This should never happen.
install failed

 

위와 같은 에러가 발생한다.

 

** 해결방법 **

.channels 디렉토리를 백업해 두고 다시 설치한 후 인스톨 한다.

1. .channels 백업

[root@SERVER php]# mv .channels .channels-broken

2. .channels 업데이트

[root@SERVER php]# pear update-channels
Updating channel "doc.php.net"
Update of Channel "doc.php.net" succeeded
Updating channel "pear.php.net"
Update of Channel "pear.php.net" succeeded
Updating channel "pecl.php.net"
Update of Channel "pecl.php.net" succeeded
3. 설치 하고자 하는게 있는지 확인 후

[root@SERVER php]# pear list
Installed packages, channel pear.php.net:
=========================================
Package          Version State
Archive_Tar      1.3.3   stable
Console_Getopt   1.2.3   stable
PEAR             1.8.0   stable
Structures_Graph 1.0.2   stable
XML_Util         1.2.1   stable

 

4. 설치 (OLE)

[root@SERVER]# pear install ole-beta
downloading OLE-1.0.0RC1.tgz ...
Starting to download OLE-1.0.0RC1.tgz (12,153 bytes)
.....done: 12,153 bytes
install ok: channel://pear.php.net/OLE-1.0.0RC1

 

5. 설치(Spreadsheet_Excel_Writer)

[root@SERVER php]# pear install Spreadsheet_Excel_Writer-beta
downloading Spreadsheet_Excel_Writer-0.9.2.tgz ...
Starting to download Spreadsheet_Excel_Writer-0.9.2.tgz (57,629 bytes)
..............done: 57,629 bytes
install ok: channel://pear.php.net/Spreadsheet_Excel_Writer-0.9.2

 

6. 설치 확인

[root@SERVER php]# pear list
Installed packages, channel pear.php.net:
=========================================
Package                  Version  State
Archive_Tar              1.3.3    stable
Console_Getopt         1.2.3    stable
OLE                          1.0.0RC1 beta
PEAR                        1.8.0    stable
Spreadsheet_Excel_Writer 0.9.2    beta
Structures_Graph        1.0.2    stable
XML_Util                    1.2.1    stable

 

 

 

======================= php적용 예제 =============================================================

<?php
  
 require_once 'Spreadsheet/Excel/Writer.php';

 $file_name = date("ymdHi").".xls";

 header("Content-type: application/vnd.ms-excel");
 header("Content-Disposition: attachment; filename=$file_name");
 header("Content-Description: PHP4 Generated Data");

 

$workbook = new Spreadsheet_Excel_Writer();

 // sending HTTP headers
 $workbook->send($file_name);
 $workbook->setVersion (8);     // 버전(Excel 97/2000)
 

 // Creating a worksheet
 $worksheet =& $workbook->addWorksheet('sheet1');
 $worksheet->setInputEncoding('EUC-KR'); // 한글 인코딩
 

 $worksheet->write(0, 0,"제목1");
 $worksheet->write(0, 1,"제목2");
 $worksheet->write(0, 2,"제목3");

 

 $worksheet->write(1, 0,"내용1");
 $worksheet->write(1, 1,"내용2");
 $worksheet->write(1, 2,"내용3");


 $workbook->close();

?>

* 홈페이지
http://pear.php.net/package/Spreadsheet_Excel_Writer

<?php


$dir="./files/";    /////////////////////////////파일이 저장된 폴더

$tmp_dir="./files/tmp/";  /////////////////////파일을 다운로드할때 쓰는 임의폴더

$filename="다운로드 할 파일이름"; ///////////말그대로 파일이름
copy($dir.$filename,$tmp_dir.$filename) or die("복사실패");
header("Content-Type: application/octet-stream");
Header("Content-Disposition: attachment;; filename=$filename");
header("Content-Transfer-Encoding: binary"); 
Header("Content-Length: ".(string)(filesize($tmp_dir.$filename))); 
Header("Cache-Control: cache, must-revalidate"); 
header("Pragma: no-cache"); 
header("Expires: 0");

$fp = fopen($tmp_dir.$filename, "rb"); //rb 읽기전용 바이러니 타입
   while(!feof($fp)) { 
       echo fread($fp, 100*1024); //echo는 전송뜯함.       
   } 
fclose ($fp);
unlink($tmp_dir.$filename) or die("삭제실패");
flush(); //출력 버퍼비우기 함수.. 
?>


출처 :  http://regexp.tistory.com/entry/PHP-파일-다운로드

string sprintf ( string format [, mixed args])

포맷 문자열 format에 따라 생성한 문자열을 반환합니다.

포맷 문자열은 0개 이상의 지시어를 조합합니다: 일반 문자는 (%을 제외하고) 결과에 그대로 복사하고, 변환 특정어는 각각의 인자로 교체한 결과를 가집니다. 이는 sprintf()printf()에 모두 적용됩니다.

각각의 변환 특정어는 퍼센트 기호(%)에 다음의 요소들이 붙어서 구성됩니다:

  1. 선택적인 패딩 지정어는 적합한 문자열의 크기를 얻기 위한 패딩에 사용하는 문자를 지정합니다. 이는 스페이스 문자나 0 (제로 문자)일 수 있습니다. 기본값은 스페이스로 채웁니다. 다른 패딩 문자는 작은 따옴표(')를 앞에 붙여서 지정할 수 있습니다. 아래의 예제를 참고하십시오.

  2. 선택적인 정렬 지정어는 결과를 왼쪽 정렬할지, 오른쪽 정렬할 지를 결정합니다. 기본값은 오른쪽 정렬입니다; 여기에 - 문자를 사용하면 왼쪽 정렬이 됩니다.

  3. 선택적인 수, 너비 지정어는 얼마나 많은 문자(최소한)가 결과에 들어갈지를 결정합니다.

  4. 선택적인 정밀도 지정어는 부동 소수점 실수에서 얼마나 많은 소수점 아래의 수를 표시할지를 결정합니다. 이 옵션은 float형 이외에는 영향을 주지 않습니다. (수를 포맷하는 다른 유용한 함수에는 number_format()이 존재합니다.)

  5. 형 지정어는 인자 데이터를 어떤 형으로 취급할지 결정합니다. 가능한 형은:

    % - 퍼센트 문자. 인자는 필요하지 않습니다.
    b - 인자를 정수로 취급하고, 2진수로 표현합니다.
    c - 인자를 정수로 취급하고, ASCII 값에 해당하는 문자로 표현합니다.
    d - 인자를 정수로 취급하고, (부호 있는) 10진수로 표현합니다.
    u - 인자를 정수로 취급하고, 부호 없는 10진수로 표현합니다.
    f - 인자를 float로 취급하고, 실수로 표현합니다.
    o - 인자를 정수로 취급하고, 8진수로 표현합니다.
    s - 인자를 문자열로 취급하고 표현합니다.
    x - 인자를 정수로 취급하고 16진수(소문자 표시)로 표현합니다.
    X - 인자를 정수로 취급하고, 16진수(대문자 표시)로 표현합니다.

PHP 4.0.6부터 포맷 문자열은 인자 넘버링/교환을 지원합니다. 다음은 예제입니다:

예 1. 인자 교환

<?php
$format
= "There are %d monkeys in the %s"
;
printf($format, $num, $location
);
?>
이는 "There are 5 monkeys in the tree"를 출력할 것입니다. 그러나, 국제화를 위해서 포맷 문자열을 별도의 파일로 작성하고, 이것을 다음과 같이 재작성했다고 생각해봅시다:

예 2. 인자 교환

<?php
$format
= "The %s contains %d monkeys"
;
printf($format, $num, $location
);
?>
문제가 생겼습니다. 포맷 문자열에서의 순서와 코드에서 인자의 순서가 일치하지 않습니다. 여기서 코드를 수정하지 않고, 단순히 포맷 문자열에 어떤 인자를 가져올지를 지정할 수 있습니다. 포맷 문자열을 다음과 같이 작성하면 됩니다:

예 3. 인자 교환

<?php
$format
= "The %2\$s contains %1\$d monkeys"
;
printf($format, $num, $location
);
?>
코드에 많은 인자를 추가하지 않고도 반복해서 출력할 수 있다는 추가적인 장점이 존재합니다. 예를 들면:

예 4. 인자 교환

<?php
$format
=
"The %2\$s contains %1\$d monkeys.
           That's a nice %2\$s full of %1\$d monkeys."
;
printf($format, $num, $location
);
?>

참고: printf(), sscanf(), fscanf(), vsprintf(), number_format().

예제

예 5. sprintf(): 제로로 채운 정수

<?php
$isodate
= sprintf("%04d-%02d-%02d", $year, $month, $day
);
?>

예 6. sprintf(): 통화 표현하기

<?php
$money1
= 68.75
;
$money2 = 54.35
;
$money = $money1 + $money2
;
// echo $money 는 "123.1"를 출력합니다;
$formatted = sprintf("%01.2f", $money
);
// echo $formatted 는 "123.10"를 출력합니다.
?>


sprintf
 
rex
16-Jun-2004 03:47
Note, if you are just looking for something to pad out a string consider str_pad.

From testing, it seems faster and was more intuitive to use (for example, making it pad the begining or end of a string... with sprintf you would have to use negative indexes)
php at sharpdreams dot com
09-May-2004 03:13
Note that when using the argument swapping, you MUST number every argument, otherwise sprintf gets confused. This only happens if you use number arguments first, then switch to a non-numbered, and then back to a numbered one.

<?php
$sql
= sprintf( "select * from %1\$s left join %2\$s on( %1\$s.id = %2\$s.midpoint ) where %1\$s.name like '%%%s%%' and %2\$s.tagname is not null", "table1", "table2", "bob"
);
// Wont work:
// Sprintf will complain about not enough arguments.
$sql = sprintf( "select * from %1\$s left join %2\$s on( %1\$s.id = %2\$s.midpoint ) where %1\$s.name like '%%%3\$s%%' and %2\$s.tagname is not null", "table1", "table2", "bob"
);
// Will work: note the %3\$s
?>
tobias at silverxnet dot de
17-Apr-2004 08:09
Regarding the previous posting:
I just wanted to give an explanation. This should be because the float to string / integer to string conversion (you are using a string, multiplying it with a float value what php automatically causes to convert the string to a float value). This is a general "problem" (or not), but not that hard to explain.
Where an integer or float starts with 0, in a string it does obviously with 1. So if you are using a string your value will increase by one (You started with a string, so it does not increase but contain the real result. If you start using a float value by not using '' around the value, you have to output the float value as well. This is just the PHP conversion.)

Try putting
$x = strval( $x );
after
$x = $x * 100;
and using your example again. You will see that the output will change to 13664 = 13664 because of the general string conversion. It seems that PHP is converting a float to a string by inceasing by one. By doing the same with intval instead of strval the output changes to 13663 = 13663.

! sprintf seems to behave wrong when using the conversation to an integer value and NOT doing the conversation at all. So use intval to convert to an integer value or strval to convert to a string value BEFORE using sprintf. This should be solving the problems.
kekec at kukac dot hu
29-Mar-2004 11:16
A really working one:
<?php
function cutzero($value
) {
   return
preg_replace("/(\.?)0+$/", "", $value
);
}
?>
06-Mar-2004 12:54
both of your cut-zero functions are just way too complicated. if it's a string where only the zeros at the end should be truncated, why not use a syntax as simple as rtrim("4.7000","0") ?
martin at schwedes dot com
10-Jul-2003 04:45
decision within sprintf:

$a = "today";
$b = sprintf('This is %s', $a=='today' ? 'today':'not today');
echo $b;
// result: This is today
Rene dot Leonhardt at agritec24 dot com
16-May-2003 11:02
Your cutzero function could be faster ;-)
   return (double)$value;

But if you must have a function:
   return preg_replace('/0+$/', '', $value);
kouber at php dot net
08-May-2003 03:55
If you want to cut all the zeros off the end of a float, but not losing any sensitive information, use this:

<?
function cutzero($value
) {
   return
preg_replace("/(\.\d+?)0+$/", "$1", $value)*1
;
}
?>

Some examples:

<?
cutzero
("4.7600");   
// returns 4.76
cutzero("4.7604")     
// returns 4.7604
cutzero("4.7000");   
// returns 4.7
cutzero("4.0000");   
// returns 4
?>
retestro_REMOVE at SPAM_esperanto dot org dot il
01-Mar-2003 08:59
Pay attention, that PHP's printf() and sprintf() do NOT support special flags, as the '+' one (makes a sign appear before the number, regardless of whether it's negative or positive).
It took me a while to figure that out: you have to write it yourself...

use something similar to the following

($value >= 0) ? printf('+%d', $value) : printf('%d', $value);
info at nospam dot webtip dot dk
18-Feb-2003 07:06
If you want to format a phonenumber with spaces, use chunk_split() which splits a string into smaller chunks. It's much simpler than using sprintf.

$phone = "12345678";

chunk_split ($phone, 2);

will return 12 34 56 78
moritz dot geselle at invision-team dot de
03-Dec-2002 04:52
a little note to the argument swapping examples which took me a while to get:
if you use single quotes for the format string (like you should do, since there aren't any variable conversions to do as long as you don't need any special chars), the given examples won't work because of the backslash before the $ (needs to be escaped in double quoted strings - but not in single quoted!)

so this:

$format = "The %2\$s contains %1\$d monkeys";
printf($format,$num,$location);

with a single quoted format string would look like this:

$format = 'The %2$s contains %1$d monkeys';
printf($format,$num,$location);

(no escapes)

I hope that helps to avoid confusion ;)
no dot email dot address at example dot com
16-Sep-2002 07:29
Using argument swapping in sprintf() with gettext: Let's say you've written the following script:

<?php
$var
= sprintf(gettext("The %2\$s contains %1\$d monkeys"), 2, "cage"
);
?>

Now you run xgettext in order to generate a .po file. The .po file will then look like this:

#: file.php:9
#, ycp-format
msgid "The %2\\$s contains %1\\$d monkeys"
msgstr ""

Notice how an extra backslash has been added by xgettext.

Once you've translated the string, you must remove all backslashes from the ID string as well as the translation, so the po file will look like this:

#: file.php:9
#, ycp-format
msgid "The %2$s contains %1$d monkeys"
msgstr "Der er %1$d aber i %2$s"

Now run msgfmt to generate the .mo file, restart Apache to remove the gettext cache if necessary, and you're off.
abiltcliffe at bigfoot.com
11-Sep-2002 12:01
To jrust at rustyparts.com, note that if you're using a double-quoted string and *don't* escape the dollar sign with a backslash, $s and $d will be interpreted as variable references. The backslash isn't part of the format specifier itself but you do need to include it when you write the format string (unless you use single quotes).
Andrew dot Wright at spamsux dot atnf dot csiro dot au
03-Jul-2002 03:22
An error in my last example:
$b = sprintf("%30.s", $a);
will only add enough spaces before $a to pad the spaces + strlen($a) to 30 places.

My method of centering fixed text in a 72 character width space is:

$a = "Some string here";
$lwidth = 36; // 72/2
$b = sprintf("%".($lwidth + round(strlen($a)/2)).".s", $a);
eden_zero_x at hotmail dot com
27-Jun-2002 03:05
Well I came up with this one, extremely simple. instead of writing <span class="class">hello</a>
you can write: print class('class','hello'); using sprintf
-----------------------------
function class_ ($class, $text=false)
 {
   return sprintf ("<span class=\"%s\">%s</span>",
       $class,
       ($text ? $text : $class)
   );
 }
-----------------------------
shgyn at binabakti dot or dot id
01-Jun-2002 08:57
Previously submitted sci() function to get scientific representation of a number is not working with 0 and negative numbers. So, here is the modified version:

function sci($x, $d=-1) {
   $min=($x<0)?"-":"";
   $x=abs($x); 
   $e=floor(($x!=0)?log10($x):0);
   $x*=pow(10,-$e);
   $fmt=($d>=0)?".".$d:"";
   $e=($e>=0)?"+".sprintf("%02d",$e):"-".sprintf("%02d",-$e);
   return sprintf("$min%".$fmt."fe%s",$x,$e);
}
fuchschr at surfeu dot at
20-Feb-2002 10:54
To have a string with leading zeros use this:
$string_i = sprintf("%04s",$value)

Gives you an output with leading zeros and 4 digits.
i.e.
0001
0002
...
0010
an so on
cv at corbach dot de
10-Feb-2002 09:36
To make radu.rendec@ines.ro's excellent function work on signed numbers you must change the first line to:

$e = floor(log10(abs($x)));
radu dot rendec at ines dot ro
09-Jan-2002 08:49
The 'e' format specifier is not documented. However, it seems to work, but without showing the exponential part of the number.

This is a function to get the scientific representation of a number:

function sci($x, $d=-1) {
   $e=floor(log10($x));
   $x*=pow(10,-$e);
   $fmt=($d>=0)?".".$d:"";
   $e=($e>=0)?"+".sprintf("%02d",$e):"-".sprintf("%02d",-$e);
   return sprintf("%".$fmt."fe%s",$x,$e);
}

It takes the number as the first parameter and the precision as the second. The precision is optional. The default precision for the 'f' format specifier is used if no precision is specified.
anqmb(at)yahoo.co.jp
05-Dec-2001 06:51
Watch out the mysterious rounding rule.
<?php
$a
= 4.5
;
$b = sprintf("%d",$a
);
$c = 4.5
;
$d = sprintf("%.0f",$c
);
$e = 0.45
;
$f = sprintf("%.1f",$e
);
print (
"$b,$d,$f\n"
);
?>

The code above prints "4,5,0.5".
(Perl version prints "4,4,0.5".)
keeper at odi dot com dot br
27-Nov-2001 11:26
Took me a while to find this out.
hope will save someones time.
IT ADD A CARACRER TO THE END OF A STRING

$x = sprintf("%'x-10s", "a");
echo $x;
target_rex at hotmail dot com
15-Apr-2001 08:20
$a = 5;
// $a is a int
echo $5;
// Outputs:"5";
// If you would like to print $a as a bin,(101) like: 00000101 (8 digits)
sprintf("%8b", $a) // Witch returns exatly 00000101 (8 digits)

// My function looked like:
  //////////////////////////////////////////
  // By DrRex - www.DrRex.dk - 15/04-2001 //
  // string bin(int dec)                  //
  //////////////////////////////////////////
  function bin($dec){
   $bin = sprintf("%8b", $dec);
   return $bin;
  }
  //////////////////////////////////////////

// Very short exampels how to use bin()

  echo "\n1. 128(10) == ".bin(128)."(2)";

  $hits = 100;
  echo "\n2. Loaded ".bin($hits)."(2) times! Bib!";

// Not very usefull, nobody understands the number, exept if small counters like this one. If it wasn't 8(2) but FFFFFF(16) digits. I would give up...

// This would output:
  1. 128(10) == 10000000(2)
  2. Loaded 01100100(2) times! Bib!

-------------------------------------------------
Greetings from Christiania, Copenhagen, Denmark!
All code by DrRex
www.DrRex.dk
ruptured at ditzed dot org
13-Apr-2001 07:45
presumably, if you wanted to fill out a HTML line with space, you could use:

$fill = " "; // what to fill
$digits = sprintf ("%'{$fill}d", $digit);

(oh, thanks to whoever above who I just copy and pasted these lines from).

=ruptured=
tjchamberlain.hotmail@com
26-Mar-2001 01:16
It is worth noting that "%5.2f" will result in a string 8 characters long (5 then the '.' then 2), not 5 characters as you might expect.
prolixmp3 at navigators dot lv
24-Mar-2001 12:55
If you are going to create a counter which uses _symbols_ before actual digits (see, f.e., SpyLog.com counters - they are filling space with "." before, so the count like 12345 looks like "........12345"), you can use the following:

$txt = "Abracadabra"; // actual string
$fit = 16; // how many digits to use
$fill = "."; // what to fill
$digits = sprintf ("%'{$fill}{$fit}s", $txt);

Paul (a.k.a. Mr.Prolix)
eblade at blackmagik dot dynup dot net
15-Mar-2001 02:55
as far as centering a string within a field size goes:

sprintf("%-=75s", $str);
voudras at nospam dot swiftslayer dot org
17-Nov-2000 08:58
Little note about sprintf and its ilk.
if you attempt something like
$string = "dingy%sflem%dwombat";
$nbr = 5;
$name = "voudras";

$msg = sprintf("%d $string %s", $nbr, $name);

sprintf will complain about a lack in the number of arguments, this would be because of the %'s in the actual string. This can be a great benifit, but is also rather confusing if you dont realize this feature, and are passing questionable variables to sprintf (for, say perhaps logging). one way around this is using
ereg_replace("%","%%", $string); before
sending it off to sprintf. This is actually how i came across this as a problem - i had realized some time ago that i would have to test my $string for
%'s, but when running the %->%% replacement on a very large serialized object, my application timed out.
   My solution was to use
sprintf("%d %s %s", $nbr, $string, $name);
but, there was a reason i originally had done this the other way - i suppose i'll find out soon enough
gherson (at) snet.net
18-Sep-2000 01:23
My attempt at a syntax summary in BNF:
%[ |0|'character][-][number][.number]type.
  Type is one of % (none), b (binary*), c (char*), d (int), f (double), o (octal*), s (string), x (hex*)  *Treated as integers.
paolini at kahuna dot sdsu dot edu
14-Apr-1999 02:12
Hey folks, don't forget to prefix a precision specifier with a period '.'!
Thus, to print a floating point number,
say $x, with two digits after the decimal point you would write:

printf( "%.2f", $x );
 

세션
  1) Session의 의미와 구현원리
  PHP 3.0 에서는 4.0으로 올라오면서 가장 눈이 띈 변화라면 세션을 사용할수 있다 라는거겠죠..
이제 세션의 의미를 말씀드린다면 어떤 유저가 어떤 특정 사이트를 접속하여 머물러 있는 시간이라고 말할수 있습니다. 좀 더 어렵게 설명을 한다면, 웹서버는 서로 다른 유저들로부터 서버 내에 있는 페이지를 요청이 들어올 경우에 각 유저들의 컴퓨터에 유일무이한 식별키를 쿠키의 형태로 발급하여 해당 유저가 사용자의정보 즉, 사용자의 로그인이나 기타 입력된 정보를 서버 내에 저장하여 유저가 다시접속하였을때 유저에게 발급한 식별키를 이용하여 서버쪽에 저장해놓은 정보(세션값)를 가져옴으로써 유저를 구별하는 원리로 구현된다고 볼수 있습니다.
다음은 세션과 쿠키의 차이점에 대해서 말씀드리겠습니다.
쿠키가 모든 유저나 회원들의 정보를 사용자의 시스템에 저장하는 반면에 세션은 유저의 데이터를 서버쪽에 저장하여 유저의 시스템에는 서버에 저장되어 있는 해당 사용자의 데이터를 찾아서 가져올 수 있는 식별키만를 저장하기 때문에 유저의 신상정보나 귀중한 정보가 밖으로 빠져나가는것을 막을수 있어 보안측면에서 좀더 안전하다고 할수 있는 있습니다. 
 
   2) Session 다루기
  ① 세션 저장방법

우선 PHP에서 세션 데이타를 서버 상에 저장하는 방법에 대해서 설명을하면
첫번째, 사용자의 데이터를 환경설정 파일(PHP.ini)에서 지정한 디렉토리에 파일로저장하는 방식으로 가장기본적인 방법이라고 할수 있습니다. 자 예를 들어 유저의 시스템에 저장한 식별키가 "0cd9cdv1vdf4fd4"이라면 서버 내 지정한 디렉토리에는 "sess_0cd9cdv1vdf4fd4"라는 이름을 갖는 파일로 생성하게 됩니다.
그러므로 식별키가 "0cd9cdv1vdf4fd4"인 유저가 사이트에 접속했을때 서버에서는 이 식별키에 해당하는 파일을 열어 해당사용자의 정보를 가져오게 되는 방법입니다.

두번째, 서버의 파일이 아닌 서버의 메모리에 저장하는 방식으로 파일로 저장하는 방법보다,높은 성능을 기대할수 있습니다. 특징으로 접속한 유저의 시스템에 식별키를 발급할 때 32개의 문자열로 이루어진 세션 아이디를 생성한다는 점입니다. 이 값은 유저의 시스템에 저장되며 유저가 서버에 접속했을 때 브라우저를 통해 서버에 자동 전송되므로 서버쪽에서 웹사이트에 접속해 있는 유저를 서로 구별할 수 있는 근거가 되겠죠..

세번째, 사용자가 직접 세션을 다루는 방식을 정의하는 방법으로 보통 이 방법을 이용하여 세션 데이터를 파일이나 메모리,혹은 데이타베이스에 저장하여 관리하는 방법입니다. 세번째 방식의 특징은 이렇게 생성한 세션 아이디값은 보통 쿠키 방식을 통해 사용자의 시스템에 저장한다는 점입니다. 그러나 만약에 유저의 브라우저가 쿠키를 허용하지 않도록 설정되어 있다면, 이방식은 아무런 의미가 없겠죠!따라서 이때에는 쿠키가 아닌 다른 방식으로 웹서버에 사용자의 식별키를 전달 할 수 있는 방법을 사용해야 하며 이럴 경우 보통은 웹페이지 요청시에 세션 아이디값을 get이나 post방식을 통해 함께 전달해주는 방식을 사용합니다.


② 세션 생성,등록,삭제및 종료하기

세션을 시작하는 방법은 간단하다..

<?
session_start()
?>

이렇게 세션함수를 호출하면 유저의 시스템에 이전에 발급된 유효 세션이 있는지 확인하게 되고, 검사결과 이전값이 없다면, 새로 세션아이디를 생성하고 이전에 발급한 아이디가 있고, 유효하다면, 서버에 저장한 데이터를 그대로 사용하게 되는것입니다.
이때 세션아이디는 기본적으로 PHPSESSID라는 이름을 갖는 변수에 저장됩니다.
예를 들어 세션 아이디 값이 "3dff4fdg4h43"이라면 쿠키 변수 $HTTP_COOKIE_VARS["PHPSESSID"]에 이 세션 아이디값이 저장되는 것입니다.

자 이제 값을 등록시켜봅시다.

<?
session_start()
session_register("name");
$name="선즈";
?>

세션등록은 session_register라는 함수를 사용합니다.
이렇게 등록된 세션 파일 안에는 name|s:4:"선즈"; 이라는 내용이 들어가게 됩니다.
세션 변수 삭제방법또한 간단하죠..
위에 보시면 name이라는 변수로 세션을 등록하였습니다.
반대로 삭제하시려면, session_unregister("name");이라고 쓰면 되겠죠..
마지막으로 세션를 종료하려면 session_destory();라는 함수를 쓰면됩니다.
세션을 종료한다는 의미는 현재의 세션과 관련된 모든 데이터를 삭제한다는 의미입니다. 즉 이 함수를 호출한 사용자의 세션에 등록되어 있는 모든 데이터를 완전히 삭제한다는 뜻입니다.

사실 웹서버 측에서 본다면 유저가 언제 웹사이트를 떠났는지 즉 사용자의 세션이 언제 끝났는지 만약 회원일때 로그아웃을 하지 않고 브라우저를 닫았을때 와 같이 그 시점을 알수가 없습니다. 그래서 웹서버가 사용자의 세션을 무효화시킬지를 결정하는 방법을 알려드리겠습니다. php의 세션 설정 항목중에 session.gc_maxlifetime 항목의 값을 수정해주는방법입니다. 보통은 1440초 즉 24분으로 되어있는데 이걸줄여주는방법이 있고,session.gc_probiblty란 항목 이것은 이렇게 더 이상 의미없는 세션데이터를 삭제하는 실행주기를 설정하는 항목으로 기본값은 1로 되어있지만, 1이라함은 1%를 의미합니다. 이값을 늘려주시면 됩니다.~


 
 
   3) Session 처리 함수 소개
  함수 기능
session_start 세션을 생성하는 함수
이때 반드시 알아야 할점은 사용하고자하는 스크립트 최상단에 호출해주어야 합니다.이 함수를 호출하기 전에는 어떤한 함수의 실행결과나 html코드도 먼저 브라우저에 전송되어서는 안된다는얘기죠.. 왜냐면 이 함수는 사용자의 시스템에 세션 아이디를 쿠키의 형태로 발급하기 때문입니다.
만약 이함수를 호출전에 다른 태그를 사용할경우 아래 그림과 같은 메세지가 나올것입니다.


 
session_register 현재 연결된 세션에 데이터를 저장할 변수를 등록한다
세션을 등록할때는 보통 변수를 사용하여 등록을 하게 되는데 대략 방법은 다음과 같다.
session_start()
session_register("name");
session_register("id"); $name="sunz";$id="sunz";

이때 session_register("name","id"); 이런식으로되 구문을 줄일수 있습니다. 
 
session_unregister 현재 연결된 세션에서 특정변수를 삭제시키는 함수
예시) session_unregister("변수"); 
 
session_unset 현재 연결된 세션에 등록되어 있는 모든 변수의 값을 삭제하는 함수
session_unregister랑 다른점은 변수는 그대로 두고 변수값만 지운다는것입니다.
 
session_destroy 현재의 세션을 종료시키는 함수
 
session_is_registered 세션 변수가 현재의 세션에 등록되어 있는지를 조사하는 함수
session_is_registered("string name")
 
session_save_path 세션 데이터 파일 저장되어 있는 경로를 반환하거나 지정한 디렉토리로 변경하는 함수
보통기본값은 /tmp 디렉토리로 설정되어있습니다.
여기서 유의해야할사항 몇가지!
①php.ini파일에 보시면 session.save_path = /tmp 이라는 옵션이 있는데 이것은 리눅스나 유닉스에 해당하는 디렉토리므로, 윈도우라면 D:/tmp 이런식으로 수정해야 합니다.
②또한 변경하고 하는 디렉토리가 실제 존재해야 하며 디렉토리 권한 은 반드시 777로 해주셔야 합니다.
③이런식으로 디렉토리를 변경할경우에는 해당 세션과 관련된 데이터를 참조하는 모든 스크립트에 session_save_path()함수를 호출해주셔야 합니다.
④마지막으로 세션을 시작하는 session_start()함수 전에 이값을 호출해주셔야 합니다.
 
session_name 현재의 세션 이름를 반환하거나 지정한 이름으로 변경하는 함수
이 함수는 인자 없이 호출하였을 경우 현재 세션의 이름을 반환하는 함수입니다. 그러나 인자를 지정하여 호출할 경우에는 지정한 값으로 현재 세션의 이름을 변경합니다. 기본값은 PHPSESSID라는 이름을 가지며 이 값 또한 php.ini에서 수정할수 있습니다.


session.name=PHPSESSID<--수정해주면 번거러움을 덜수있겠죠!

위의 session_save_path함수처럼 이 함수를 통해 변경한 세션이름은 함수를 호출한 스크립트내에서만 유효하므로 변경된 함수를 사용하실 곳에서는 항상 session_start()함수보다 먼저 호출해주셔야 합니다.
 
session_id 현재의 세션 아이디를 반환하거나 지정한 값으로 변경하는 함수, 현재사용하는 세션 아이디를 구할때나 사용하죠..
 
session_module_name 세션 데이터 다루는 저장 방식에 관한 정보를 반환시키는 함수
첨에 세션의 저장방식에 대해서 설명드릴때 3가지 방법이 있다고했죠,파일저장방법, 메모리저장바업,사용자가 세션을 다루는 방법을 직접정의하는방법이 세가지 방법을 값으로 변환해주는 함수입니다. 이 함수도 PHP환경설정 파일(php.ini)내에서 수정이 가능합니다.

session.save_handler = files; <---기본값이죠


 
session_get_cookie_params 세션 쿠키와 관련된 정보를 배열의 형태로 반환시키는 함수
이 함수는 세션 쿠키의 유효 기간과 이 쿠키값을 참조할 수 있는 디렉토리나 도메인 범위 등을 연관 배열의 형태로 반환합니다. 키값은 유효기간을 표시하는 lifetime과 쿠키값을 참조할수 있는 디렉토리값 path 와, 도메인 domain 키값이 있습니다. 기본적으로 lifetime은 0, path는 /, 도메인은 ''빈값으로 되어있습니다.
 
session_set_cookie_params 세션쿠키를 발급할 때 필요한 인자를 설정하는 함수
위의 get_cookie_paranms가 정보값을 배열로 반환한다면 이것을 그값들을 설정하는 함수입니다.
 
session_encode 현재의 세션에 저장되어 있는 데이터를 인코딩하여 인코딩된 문자열를 반환하는 함수입니다.
쉽게 말해서 세션을 등록시에 생기는 데이터들, 보통은 파일로저장되는 세션정보를 문자로 변환해준다는 뜻입니다. 
 
session_decode 함수의 인자로 전달한 세션 데이터를 디코딩해주는 함수
 
 
session_cache_limiter 현재 cache limiter 항목의 값으로 설정되어 있는 이름을 반환하거나 이 이름을 지정한 이름으로 변경한다.
유저의 브라우저에 전송되는 HTTP헤더를 이요하여 사용자가 요청한 웹페이지의 내용을 사용자의 시스템에 캐시로 남길 수 있도록 허용할것인지 그 허용수준을 어느정도로 할것인지를 조절하는 항목으로 PHP의 환경설정 파일(php.ini)에서도 이 cache limiter의 단계를 3단계로 구분되도록 되어있습니다. 첫째 nocache는 유저의 시스템에 cache를 보관하지 않으며, 두번째 public은 유저의 시스템에 저장하며, 셋째 private도 마찬가지로 유저가 요청한 페이지를 시스템에 cache로 남지기만 public보다는 좀 엄격합니다.
 
session_set_save_handler 세션을 다룰 수 있는 사용자 정의 함수를 구현한다.

 

간단한 세션 열고 닫기 예제..

 

<?
session_start();
@error_reporting(E_ALL ^ E_NOTICE);
@extract($HTTP_GET_VARS);
@extract($HTTP_POST_VARS);
@extract($HTTP_SERVER_VARS);
@extract($HTTP_ENV_VARS);

//처음 페이지가 로딩될 때는 $mode에 값이 없으므로 switch문은 그냥 통과된다.
echo " >>", $mode, "<< ";
switch ($mode)
{
case 'register':
// 세션_시작
// $count란 세션변수를 등록한다.
if(!session_register("count"))
 echo "세션등록에 실패하였습니다.<br>";
$HTTP_SESSION_VARS['count'] = 0;
break;

case 'count':
// 세션등록된 $count 증가
$HTTP_SESSION_VARS['count'] = $HTTP_SESSION_VARS['count'] + 1;
break;

case 'unregister':
// 등록된 세션변수 $count를 제거한다.
session_unregister("count");
break;

case 'destroy':
// 세션관계없이 페이지만 다시 불러온다.
 session_destroy();
 break;
}
?>

당신은 이 페이지를 <? echo $HTTP_SESSION_VARS['count'] ?>번째 방문하고 계십니다.<p>

<a href="session.php?mode=register">세션변수 등록</A> |
<a href="session.php?mode=count">세션변수 증가</A> |
<a href="session.php?mode=unregister">등록된 세션변수 제거</A> |
<a href="session.php?mode=destroy">세션 제거
</A><p>

<table border=1>
<tr>
<td>세션 모듈 이름</td>
<td><? echo session_module_name() ?></td>
</tr>
<tr>
<td>세션 파일이 저장된 경로</td>
<td><? echo session_save_path() ?></td>
</tr>
<tr>
<td>현재 세션의 아이디</td>
<td><? echo session_id() ?></td>
</tr>
<tr>
<td>쿠키로 저장된 세션ID</td>
<td><? echo $PHPSESSID ?></td>
</tr>
<tr>
<td>count세션변수의 등록여부</td>
<td>
<?
if(session_is_registered("count"))
echo "등록되어있습니다.";
else
echo "등록되어 있지 않습니다.<br>";
?>
</td>
</tr>
</table>

 

출처 : http://www.hompydesign.com/bbs/board.php?bo_table=php_study&wr_id=634

'Language > php' 카테고리의 다른 글

php 파일 다운로드 ....  (0) 2010.01.28
[PHP] sprintf 형식화한 문자열을 반환합니다...  (0) 2009.10.31
php + 오라클 프로시저 연동 ...  (0) 2009.07.05
php 오라클 함수 ...  (0) 2009.07.05
php 파일처리 함수 ...  (0) 2009.07.05

<?php

 $errCode = ""; // 프로시저 실행 후 넘겨받을 코드값
 $errMsg  = ""; // 프로시저 실행 후 넘겨받을 메세지
 $sql = 'BEGIN 프로시저명(:파라미터1,:파라미터2,:errCode,:errMsg); END;';
 $stmt = OCIParse($conn,$sql);
  
 OciBindByName($stmt,':파라미터1', $_POST["파라미터1"],32); 
 OciBindByName($stmt,':파라미터2', $_POST["파라미터2"],32);

 OciBindByName($stmt,':errCode',  $errCode,32); // out으로 넘겨받을 코드 예)200(성공), 500(에러)
 OciBindByName($stmt,':errMsg',  $errMsg,100);  // out으로 넘겨받을 메세지
 

OCIExecute($stmt);

OCIFreeStatement($stmt); 
OciLogoff($conn);
 /*

프로시저 실행 후 성공 또는 에러시 보여줄 페이지 및 이동 부분 구현

*/

?>

OCI8함수

* OCILogon()
오라클 데이터베이스에 접속하는 함수입니다.
사용 예)$dbcon = OCILogon("아이디","패스워드","로컬오라클인스턴스이름");

* OCIPLogon()
오라클 데이터베이스에 접속하는 함수로, 영구적 DB접속을 담당합니다.
사용 예)$dbcon = OCIPLogon("아이디","패스워드","로컬오라클인스턴스이름");


* OCIParse()
넘어온 SQL 질의(query)를 해석하는 함수입니다. 질의문이 유효하면 구문을 리턴시키고 그렇지 않으면 False를
리턴합니다.
사용 예)$stmt = OciParse($conn,$sql);

* OCIExecute()
이미 해석된 구문을 실행하는 함수입니다. 이 경우 디폴트는 OCI_COMMIT_ON_SUCCESS로 실행 즉시 commit이 됩니다.
사용 예)$result = OCIExecute($stmt);

* OCIFetch()
실행시킨 SQL 구문에서 레코드를 다음으로 이동시킵니다. 다음 레코드로 커서가 이동합니다.
사용 예)OCIFetch($stmt)

* OCIFetchInto()
다음 열로 커서를 이동하는 함수입니다. 결과값을 임의의 특정 일차원 배열 변수에 넣을 수 있습니다.
사용 예)while(OCIFetchinto($stmt,$row,OCI_ASSOC)){
               $변수명 = $row[컬럼명];
          }

Description

int OCIFetchInto ( int stmt, array & result [, int mode])

OCIFetchInto()함수는 다음열(SELECT 구문을 위해)을 result배열에 페치한다. OCIFetchInto()함수는 result 변수의 이전값을 덮어 쓸것이다. 기본적으로 result변수는 NULL이 아닌 모든 컬럼의 일차원 배열을 포함할것이다.

mode인수는 이 함수의 기본값을 변화시킨다. 한개 이상의 플래그를 추가할수있다(OCI_ASSOC+OCI_RETURN_NULLS와 같이). 알려진 플래그는 다음과 같다:

OCI_ASSOC 연관배열(associative array)을 리턴한다.
OCI_NUM 키값이 0부터 시작하는 배열을 리턴한다. (디폴트)
OCI_RETURN_NULLS 빈 컬럼도 리턴한다.
OCI_RETURN_LOBSdescriptor 대신에 LOB의 값을 리턴한다.



* OCIFetchStatement()
SQL 구문의 모든 결과값을 2차원 배열에 넣는 함수입니다. 즉, 질의의 결과를 어느 특정 이차원 배열에 모두 저장합니다.
사용 예)OCIFetchStatement($stmt1,$result);

* OCILogOff()
오라클부터 접속을 해제하는 함수입니다.
사용 예)OciLogoff($dbconn);

- 파일처리함수 -
basename -- 경로명에서 파일이름만 반환합니다
chgrp -- 파일의 그룹을 변환합니다
chmod -- 파일의 모드 변경
chown -- 파일의 소유자 변경
clearstatcache -- 파일의 통계(stat) 캐시를 삭제합니다.
copy -- 파일을 복사합니다
delete -- 실제로는 없는 명령
dirname -- 경로의 구성요소중에서 디렉토리 이름만 반환합니다.
disk_free_space -- Returns available space in directory
disk_total_space -- Returns the total size of a directory
diskfreespace -- 디렉토리의 사용가능한 공간을 반환합니다.
fclose -- 열려있는 파일 포인터를 닫습니다.
feof -- 파일의 끝이 파일포인터에 있는지 테스트합니다.
fflush -- 출력결과를 파일로 보냅니다.
fgetc -- 파일포인터로부터 문자 가져오기
fgetcsv -- 파일포인터에서 라인을 가져오고 CVS 에 맞게 변환합니다.
fgets -- 파일 포인터에서 라인 가져오기
fgetss -- 파일포인터에서 라인을 가져오고 HTML 태그를 없애기
file_exists -- 파일이 있는지 체크
file_get_contents -- Reads entire file into a string
file_put_contents -- Write a string to a file
file -- 파일전체를 배열로 읽어들임
fileatime -- 최근에 파일에 접근한 시간을 가져옴
filectime -- 파일의 아이노드 변경시간을 가져옵니다
filegroup -- 파일의 그룹을 가져옵니다
fileinode -- 파일의 아이노드를 가져옵니다
filemtime -- 파일이 수정된 시간을 가져옵니다
fileowner -- 파일의 소유자를 가져옵니다
fileperms -- 파일의 권한을 가져옵니다
filesize -- 파일의 크기를 가져옵니다
filetype -- 파일의 형식을 가져옵니다
flock -- 파일 잠김에 관한 간단한 도움말
fnmatch -- Match filename against a pattern
fopen -- 파일이나 URL을 엽니다
fpassthru -- 파일 포인터에 남아있는 모든 데이타를 출력합니다
fputs -- 파일 포인터에 기록하기
fread -- Binary-safe 파일 읽기
fscanf -- 형식에 따라서 파일로 부터 분석하여 입력하기
fseek -- 파일 포인터에서 찾기
fstat -- 오픈 파일 포인터를 사용하는 파일에 대한 정보 가져오기
ftell -- 파일포인터의 읽기/쓰기 위치 말하기
ftruncate -- 주어진 길이로 잘라내기
fwrite -- Binary-safe 파일 쓰기
glob -- Find pathnames matching a pattern
is_dir -- filename 이 디렉토리인지 아닌지 이야기하기
is_executable -- filename이 실행가능한 것인지 아닌지 이야기하기
is_file -- filename이 보통 파일인지 아닌지 이야기하기
is_link -- filename이 심볼릭 링크인지 아닌지 이야기하기
is_readable -- filename이 읽기 가능한 것인지 아닌지 이야기하기
is_uploaded_file -- file이 HTTP POST를 통해 업로드된 것인지 아닌지 이야기하기
is_writable -- filename이 쓰기가능한 것인지 아닌지 이야기하기
is_writeable -- Alias of is_writable()
link -- hard link 만들기
linkinfo -- 링크 정보 가져오기
lstat -- 파일이나 심볼릭 링크에 관한 정보를 제공
mkdir -- 디렉토리 만들기
move_uploaded_file -- 업로드된 파일을 다른곳으로 이동하기
parse_ini_file -- Parse a configuration file
pathinfo -- Returns information about a file path
pclose -- 진행되는 파일 포인터 닫기
popen -- 진행되는 파일 포인터를 열기
readfile -- 파일을 출력합니다
readlink -- symbolic link의 target 반환
realpath -- 표준화된 절대 경로명을 반환합니다
rename -- 파일을 새 이름으로 고치기
rewind -- 파일포인터의 위치를 되돌립니다(rewind).
rmdir -- 디렉토리 제거하기
set_file_buffer -- 주어진 파일 포인터에 파일 버퍼링 설정하기
stat -- file에 대한 정보 제공
symlink -- 심볼릭 링크 만들기
tempnam -- 유일한 파일 이름 만들기
tmpfile -- 임시 파일 만들기
touch -- 파일의 수정시간을 설정합니다
umask -- 현재의 umask를 변경하기
unlink -- 파일을 삭제하기

- 문자열처리함수 -
AddCSlashes -- C 형식으로 문자열에 슬래쉬를 덧붙입니다.
addslashes -- 문자열에 슬래쉬를 덧붙입니다.
bin2hex -- 바이너리 데이터를 16진수 표현으로 바꿉니다.
chop -- rtrim()의 별칭.
chr -- 특정 문자를 반환합니다.
chunk_split -- 문자열을 작은 조각으로 나눕니다.
convert_cyr_string -- 키릴 문자셋을 다른 것으로 변환합니다.
convert_uudecode -- Decode a uuencoded string
convert_uuencode -- Uuencode a string
count_chars -- 문자열 안에 사용한 문자에 대한 정보를 반환합니다.
crc32 -- 문자열의 crc32값을 계산합니다.
crypt -- 단방향 문자열 암호화(해슁).
echo -- 하나 이상의 문자열을 출력합니다.
explode -- 문자열을 주어진 문자열을 기준으로 분리합니다.
fprintf -- 문자열을 형식화하여 스트림에 기록합니다.
get_html_translation_table -- htmlspecialchars()와 htmlentities()에서 사용하는 변환표를 반환합니다.
hebrev -- 논리 헤브라이어 텍스트를 표시 텍스트로 변환합니다.
hebrevc -- 개행 문자를 포함하여 논리 헤브라이어 텍스트를 표시 텍스트로 변환합니다.
html_entity_decode -- 모든 HTML 엔티티를 해당하는 문자로 변환합니다.
htmlentities -- 해당하는 모든 문자를 HTML 엔티티로 변환합니다.
htmlspecialchars -- 특수 문자를 HTML 엔터티로 변환합니다.
implode -- 문자열로 배열 요소를 결합합니다.
join -- implode()의 별칭.
levenshtein -- 두 문자열 사이의 Levenshtein distance를 계산합니다.
localeconv -- 숫자 형식화 정보를 얻습니다.
ltrim -- 문자열 시작 부분의 공백을 제거합니다.
md5_file -- 주어진 파일명의 md5 해쉬를 계산합니다.
md5 -- 문자열의 md5 해쉬를 계산합니다.
metaphone -- 문자열의 메타폰 키를 계산합니다.
money_format -- Formats a number as a currency string
nl_langinfo -- 언어와 로케일 정보를 얻습니다.
nl2br -- 문자열의 모든 줄바꿈 앞에 HTML 줄바꿈 태그를 삽입합니다.
number_format -- Format a number with grouped thousands
ord -- 문자의 아스키 값을 반환합니다.
parse_str -- 문자열을 처리하여 변수를 생성합니다.
print -- 문자열을 출력합니다.
printf -- 형식화한 문자열을 출력합니다.
quoted_printable_decode -- 인용되어 있는 출력 가능 문자열을 8비트 문자열로 변환합니다.
quotemeta -- 메타 문자를 인용합니다.
rtrim -- 문자열 끝 부분의 공백을 제거합니다.
setlocale -- 지역적보를 지정한다.
sha1_file -- 파일의 sha1 해쉬를 계산합니다.
sha1 -- 문자열의 sha1 해쉬를 계산합니다.
similar_text -- 두 문자열 간의 유사성을 계산합니다.
soundex -- 문자열의 soundex 키를 계산합니다.
sprintf -- 형식화한 문자열을 반환합니다.
sscanf -- 문자열을 형식에 따라 처리합니다.
str_ireplace -- 대소문자를 구별하지 않는 str_replace().
str_pad -- 문자열을 어떠한 길이가 되도록 다른 문자열로 채웁니다.
str_repeat -- 문자열을 반복합니다.
str_replace -- 발견한 모든 검색 문자열을 치환 문자열로 교체합니다.
str_rot13 -- 문자열에 rot13 변환을 수행합니다.
str_shuffle -- 문자열을 랜덤하게 섞습니다.
str_split -- 문자열을 배열로 변환합니다.
str_word_count -- 문자열에서 사용한 단어에 대한 정보를 반환합니다.
strcasecmp -- 대소문자를 구별하지 않는 바이너리 호환 문자열 비교
strchr -- strstr()의 별칭.
strcmp -- 바이너리 호환 문자열 비교
strcoll -- 로케일 기반 문자열 비교
strcspn -- 마스크에 매칭하지 않는 처음 세그먼트의 길이를 찾습니다.
strip_tags -- 문자열에서 HTML과 PHP 태그를 제거합니다.
stripcslashes -- addcslashes()로 처리한 문자열을 되돌립니다.
stripos -- 대소문자를 구별하지 않고 문자열이 처음 나타나는 위치를 찾습니다.
stripslashes -- addslashes()로 처리한 문자열을 되돌립니다.
stristr -- 대소문자를 구별하지 않는 strstr()
strlen -- 문자열의 길이를 구합니다.
strnatcasecmp -- "natural order" 알고리즘을 이용한 대소문자를 구별하지 않는 문자열 비교.
strnatcmp -- "natural order" 알고리즘을 이용한 문자열 비교
strncasecmp -- 대소문자를 구별하지 않는 처음 n 문자의 이진 호환 문자열 비교
strncmp -- 처음 n 문자의 이진 호환 문자열 비교
strpbrk -- Search a string for any of a set of characters
strpos -- 문자열이 처음 나타나는 위치를 찾습니다.
strrchr -- 문자열에서 문자가 마지막으로 나오는 부분을 찾습니다.
strrev -- 문자열을 뒤집습니다.
strripos -- 문자열에서 대소문자 구별 없이 문자열이 나타나는 마지막 위치를 찾습니다.
strrpos -- 문자열에서 마지막 문자의 위치를 찾습니다.
strspn -- 마스크에 매칭되는 초기 세그먼트의 길이를 찾는다.
strstr -- 문자열이 처음으로 나타나는 부분을 찾습니다.
strtok -- 문자열을 토큰화 합니다.
strtolower -- 문자열을 소문자로 만듭니다.
strtoupper -- 문자열을 대문자로 만듭니다.
strtr -- 특정 문자를 번역한다.
substr_compare -- Binary safe optionally case insensitive comparison of 2 strings from an offset, up to length characters
substr_count -- 부분문자열의 수를 센다
substr_replace -- 문자열의 일부를 치환한다.
substr -- 문자열의 일부를 반환한다.
trim -- 문자열의 처음과 끝에 있는 공백을 제거한다.
ucfirst -- 문자열의 처음 글자를 대문자로 만든다.
ucwords -- 문자열에 있는 각 단어의 처음 글자를 대문자로 바꾼다.
vprintf -- Output a formatted string
vsprintf -- Return a formatted string
wordwrap -- 정지문자를 이용해 주어진 수 만큼의 문자를 래핑한다.

데이터베이스, PHP를 만나면「알짜 사이트로 부활!」
- PHP는 거의 모든 데이터베이스를 지원한다. 많은 프로젝트에서 PHPer들은 다양한 데이터베이스 연동에 대한 요구를 받는다. 하지만 기초를 다지거나, 기술력을 높이기 위한 전략적인 프로그래밍을 하다 보면 하나의 데이터베이스에 의존하는 경우가 많다. 개발자들에게 요구되는 기술력이라는 것은 결국 다양한 경험에 기반을 둔다고 볼 수 있다. 진정한 개발자의 모습은 어떤 것일까? 언제나 다양성 앞에 놓여 있고, 복잡성에 도전하는 게 개발자의 모습이 아닐까라는 생각을 하면서 이 글을 시작한다.

웹의 무수한 확장력은 가공할 만큼 커지고 있다. 여기에 부각되는 다양한 언어들이 수많은 현장에서 SI라는 명분으로, 웹이라는 명분으로, 인터넷과 인트라넷이라는 명분으로 쓰여지고 대중화되고 있다. PHP 또한 이러한 확장력에 힘을 싣고 있다.

PHP의 핵심적인 확장력은 무엇일까? 여러 가지가 있을 수 있겠지만 단연히 데이터베이스에 대한 강력한 핸들링이라는 측면을 들 수 있겠다. PHP는 거의 모든 데이터베이스를 지원한다. 이번 호에서는 데이터베이스에 대한 핸들링과 나눌 수 있는 팁이 있다면 함께 고민해 보는 자리로 만들고자 한다.

상세한 데이터베이스의 설치나 DBA(DataBase Administrator)가 다루는 기법은 다루지 않겠다. 하지만 기존의 책자에 다양하게 소개되지 않은 부분이 있다면 그것을 다루어 봤으면 한다. 다루고자 하는 데이터베이스는 가장 많이 사용되는 MySQL, 윈도우 진영의 MSSQL, 대형 프로젝트에 자주 사용되는 오라클, 사이베이스로 분류해 진행하고자 한다.

가장 많이 다뤄진 MySQL과 궁합 맞추기
PHP와 데이터베이스라면 역시 MySQL을 들 수 있다. 중소 사이트에선 거의 MySQL이 사용되었다고 해도 과언이 아닐 정도로 많은 분야에서 MySQL이 사용되고 있다. 또한 MySQL은 리눅스 버전이 아니고 윈도우 버전도 계속 업데이트되면서 그 확장력을 확보하고 있다. 자료를 찾지 못해 이번 호에 게재를 못했는데 얼마 전 데이터베이스 벤치마킹에서 오라클 9i와 MySQL이 성능 및 속도에서 1위를 했다는 기사를 보았었다. 이렇듯 MySQL은 계속 확장하는 큰 물결이라고 볼 수 있다. 관련 프로그램도 많이 지원되는데 일례로 MySQL Front 같은 유틸리티는 MSSQL 엔터프라이즈 관리자와 같은 UI를 제공해 개발자에게 편리한 개발환경을 제공하기도 한다.

MySQL은 기존에도 많이 다루었던 부분임으로 간단한 사용 루틴을 설명하고 나머지 부분은 MySQL 팁에 대해 다뤄보도록 하겠다. 데이터베이스 프로그래밍은 SELECT, UPDATE, DELETE문을 이용해 화면에 어떻게 뿌려주는가가 가장 기본적인 컨셉이다. 간단한 웹 게시판 리스팅 소스 코드를 보자.

<?
       mysql_connect("host","user","pass");
       mysql_select_db("user_db");
       $query = mysql_query("select * from web_board");
       $all = mssql_num_rows($query);

           for($i=0;$i<$all;$i++)
           {
              $seek = mysql_data_seek($query,$i);
              $row = mysql_fetch_array($query);
              $number = $row[number];
              $nam = $row[name];
              $date = chop($row[write_date]);
              $title = $row[title];
              $conten = $row[content];
              $hit = $row[hit];
           }//ending of for loof
?>


단순히 테이블을 SELETE한 후 리스트를 가져오는 구문이다. 경험상 이 구문은 모든 웹 프로그램의 기본이다. 데이터베이스 프로그래밍에서 HTML. 스크립트를 포함한 기법을 제외하고는 모두 이 틀에 기초하고 있다고 해도 과언이 아니다. 앞 코드도 DB 연결 -> SQL 실행 -> 루프 -> 순차적 데이터 추출의 과정이다. 물론 좀더 정교한 프로그래밍을 한다면, WHERE 조건문, GROUP BY 등 SQL이 정밀하게 수정돼야 하고 리스팅을 위해 페이징 기법도 추가될 것이다.

여기서는 단순한 구조만 이해하고 넘어가도록 하겠다. 너무 많이 다룬 부분일 것 같아서 MySQL에 대한 특별한 설명은 하지 않겠다. 여기서는 mysql_data_seek() 함수와 mssql_fetch_array() 함수만 이해하면 될 듯 하다. mysql_data_seek는 MySQL 쿼리 결과의 커서의 위치를 가리키는 함수이다. 예를 들어 쿼리의 결과가 다섯 개가 나왔다면 이것을 순차적으로 추출하기 위해 mysql_data_seek() 함수를 이용해 첫 번째 인자에는 유효한 SQL 쿼리문을 넣고, 두 번째 인자에는 커서의 위치를 넣는 것이다.

0을 넣게 되면 첫 번째 row를 가져올 것이다. mssql_fetch_array() 함수는 0번째 row를 배열에 담는 과정이다. 이렇게 해서 $row에서 추출해 데이터 정보를 정렬하면 된다. 이 부분만 짚고 넘어가도 충분히 이해할 수 있으리라 생각된다. 또한 UPDATE, DELETE문은 따로 함수나 구문을 이용하는 것이 아니고 mysql_query() 함수만 사용하면 쿼리를 실행할 수 있다.

데이터베이스 프로그래밍할 때 가장 시간이 오래 걸리는 것은 관련 SQL문을 만드는 것과 실제로 데이터 값이 POST나 GET으로 잘 넘어가는지를 체크하고 디버깅하는 경우다. 이 시간을 줄이기 위해 어떤 개발자는 POST나 GET으로 넘어가는 모든 값을 출력해 주는 디버깅 툴을 간단히 만들어서 사용하기도 한다. MySQL은 이런 개발을 지원하는 툴이나 프로그램이 많이 제공된다. 개발자가 조금만 신경쓰면 개발에 많은 도움이 될 것이라고 생각된다. 최신 정보를 잘 활용하자.

좀더 빠르게! MySQL 데이터베이스 활용법
  ● *를 사용하지 마라
무심코 개발자들은 SELETE문을 던질 때 * 기호를 많이 쓴다. 하지만 이것은 관련되어 있는 필드를 모두 가져오는 행위이기 때문에 비효율적이라고 할 수 있겠다. 다음과 같이 필요한 필드만 불러와서 효율적인 쿼리를 만들도록 하자.

“SELECT 필드 1, 필드 2 FROM 테이블 명”

  ● mysql_result() 함수보다는 mysql_data_seek()를 사용하자
$date = mysql_result($query_result, $i, 0); 소스를 보면 $i개의 0번째 컬럼을 호출한다. 이런 방법은 컬럼 개수가 맞지 않거나, 정확한 컬럼의 특징(제목)을 알아내기가 힘들고 컬럼의 업데이트가 발생했을 때 프로그램 전체를 수정하는 문제가 발생할 수도 있다. 따라서 앞의 리스트와 같이 mysql_data_seek를 선택해 row에 배열로 담아서 꺼내면, 잘못 호출했을 때 null로 인식하기 때문에 오류를 줄일 수 있고, 데이터베이스의 수정이 발생하더라도 해당 필드의 정보만 추가하면 된다.

  ● limit를 잘 활용하자
MySQL에서는 LIMIT 속성을 제공한다. LIMIT는 정확히 그 개수만큼 가져온 뒤에 테이블의 나머지 레코드에 대해서는 WHERE 조건문에 만족하는 레코드가 있는지 검사하지 않아도 되므로 속도가 빠르다. 다시 말해 속도 차이는 LIMIT를 만족하면, 바로 쿼리 실행을 중지하므로 더 이상 WHERE 검사를 하지 않는 데서 발생한다.

  ● 인덱스를 사용하자
인덱스를 사용하고 하지 않고는 속도에서 엄청난 차이가 난다. MySQL의 모든 인덱스(PRIMARY, UNIQUE AND INDEX())는 B-트리에 저장된다. 문자열은 자동으로 앞뒤의 공간이 압축된다. 인덱스는 다음과 같은 곳에 사용하면 아주 유용하다.

     ◆ WHERE문에서 해당하는 레코드 빨리 찾기
     ◆ 조인을 수행할 때 다른 테이블에서 레코드 가져오기
     ◆ 특정 키에서 MAX()나 MIN() 값 찾기
     ◆ 정렬이나 그룹화할 때 인덱스 키를 사용하면 테이블을 정렬하거나 그룹화한다.
         키에 DESC가 붙으면 역순으로 인덱스를 읽는다.
     ◆ 어떤 경우에는 데이터 파일에 묻지 않고 값을 가져온다. 어떤 테이블에서 사용
         하는 모든 컬럼이 숫자이고 특정 키로 형성되어 있으면 빠른 속도로
         인덱스 트리에서 값을 가져올 수 있다.

  ● varchar와 char의 차이를 이해하자
varchar는 가변형 컬럼이고, char는 고정형 컬럼이다. 속도는 테이블 컬럼이 고정된 즉, varchar 같은 타입이나 bolb, text 타입이 들어가지 않는 고정 테이블이 빠르다. char형은 고정 길이이기 때문에 속도 면에서 빠르며, 이유는 메모리 사용이나 스토리지(HDD) 사용 면에서 일정한 길이이기 때문이다. 스토리지에 부담이 없는 프로젝트라면 char형 같은 고정 길이를 추천한다.

MSSQL로 PHP를 윈도우 서버에서 개발하자
윈도우 서버는 광범위하게 쓰여지고 있다. 많은 개발자들이 PHP를 윈도우 서버에 포팅시켜 솔루션을 개발하고 있다. 윈도우 서버의 대표적인 데이터베이스가 MSSQL이다. MSSQL은 상용 데이터베이스로 편리한 UI를 제공해 관리자 및 개발자가 사용하기에 편리한 장점이 있다.

혹자는 MSSQL과 PHP를 연동하면 윈도우 계열이기 때문에 많이 느리다고 말하는데, 필자의 경우 윈도우에서 PHP를 많이 개발해 본 결과 실제 ASP보다 느리다고 생각한 적은 한 번도 없었다. 그 정도로 PHP는 넓은 확장성을 가지는 장점이 있다고 말하고 싶다. MSSQL 연동의 기본적인 골격을 살펴보자.

<?
       mssql_connect("host","user","pass");
       mssql_select_db("user_db");
       $query = mssql_query("select * from web_board");
       $all = mssql_num_rows($query);

           for($i=0;$i<$all;$i++)
           {
              $seek = mssql_data_seek($query,$i);
              $row = mssql_fetch_array($query);
              $number = $row[number];
              $name = $row[name];
              $date = $row[write_date];
              $title = $row[title];
              $content = $row[content];
              $hit = $row[hit];
           }//ending of for loof
?>


독자들은 한눈에 MySQL과 MSSQL이 거의 유사하다는 것을 알 수 있다. 아마도 PHP 라이브러리 개발자들의 배려이지 않을까 싶다. 개발자들은 mysql이라는 글자를 mssql로만 바꾸면 바로 mssql에 포팅시킬 수 있다. msslq_connect() 함수로 서버에 접속하고 mssql_select_db()로 데이터베이스를 선택한다. mssql_query()문은 SQL문을 실행시키는 함수이다. 이 쿼리를 mssql_num_rows() 함수를 이용해 전체 행의 개수를 구한다. for문에서 sql_data_seek() 함수로 $i번째 row를 선택해 mssql_fetch_array() 함수를 이용해 $row 배열에 담아 출력하는 루틴이다.

MSSQL은 윈도우 환경이기 때문에 개발을 더욱 직관적으로 진행할 수 있는 장점이 있다. 이러한 장점과 함께 제공하는 다양한 기능을 활용함으로써 최적의 사이트를 구축할 수 있을 것이다.

MS SQL 활용하기
  ● 스토어 프로시저를 활용하자
데이터베이스 프로그래밍을 하다 보면 통계나 축적치에 대한 결과를 보아야 할 경우가 많다. 예를 들면 일별 통계, 월별 통계 같은 경우가 그런 것이다. 우리가 일반적으로 count나 sum, avg로 통계를 내고자 할 경우 많은 데이터 양으로 인해 제대로 된 결과를 시간내에 볼 수 없는 경우가 많다. 필자는 프로젝트시에 통계 관련 모든 데이터는 시간, 일, 월의 통계를 낼 수 있는 스토어 프로시저를 만들어 작업 일정에 추가해 주기적으로 자동 작동하도록 만든다. 이렇게 하면 아주 빠른 시간내에 만들고 싶은 통계 데이터를 쉽게 만들 수 있다. 데이터베이스에서 제시하는 스토어 프로시저의 장점은 다음과 같다.

     ◆ 여러 클라이언트간의 업무 규칙 공유
     ◆ 데이터베이스 내부 구조 감추기
     ◆ 서버 보호, 데이터 통합 구현
     ◆ 쿼리 처리 속도 향상
     ◆ 네트워크 트래픽 감소

  ● 뷰를 잘 활용하자
뷰도 마찬가지로 공통 업무나 반복적인 쿼리 또는 아주 복잡한 쿼리를 위해 꼭 필요하다. 뷰는 SELECT문을 바탕으로 한 일종의 가상 테이블이다. 하지만 뷰는 테이블이나 인덱스처럼 자체 데이터를 저장하기 위한 저장 영역을 사용하지 않는다. 뷰는 다만 사용자 정의 데이터 타입, 디폴트, 룰처럼 자신의 정의만이 해당 데이터베이스 내의 sysobjects, syscomments등 시스템 테이블에 레코드로서 저장되어 있다. 뷰의 장점은 다음과 같다.

     ◆ 뷰는 일반 사용자 또는 개발자들에게 필요한 정보를 아주 쉽게 제공해 줄 수 있다.
     ◆ 뷰가 참조하는 테이블 구조가 바뀌더라도 쿼리문을 수정할 필요가 없다.
     ◆ 뷰는 보안과 관련해 테이블에서 사용자들에게 필요한 레코드, 필요한 컬럼 데이터만을 선별적으로 보여줄 수 있다.

간단히 정리하면 뷰는 복잡한 SELECT 쿼리문을 아주 단순하게 만들어 서버에 저장해 둔 것이다. 이것은 클라이언트가 그렇게 복잡한 SELET문을 반복적으로 사용하기 싫을 때 사용하면 매우 편리하다. PHP에서 데이터베이스를 액세스할 때는 스크립트 내에 복잡한 SELECT문을 사용하지 않는 것이 좋다. 이것은 스크립트 파일의 크기를 늘리고 스크립트의 가독성과 보안을 떨어뜨리며 스크립트의 속도 저하의 원인이 되기도 한다.

  ● 트리거를 활용하자
트리거는 자기가 종속된 특정 테이블에서 데이터의 변화가 발생했을 때 자동으로 실행되는 아주 특별한 종류의 스토어 프로시저이다. 어떤 테이블의 데이터가 INSERT, UPDATE 또는 DELETE문을 만나 변화가 생길 때 자동으로 실행되게끔 정의해 놓은 Transact-SQL문의 집합이라고 볼 수 있다. 트리거는 다음과 같은 장점을 제공한다.

     ◆ 복잡한 데이터 통합을 구현해 준다.
     ◆ 역정규화된(denormalized) 데이터 관리를 해준다.
     ◆ 복잡한 업무 규칙을 단순화시킬 수 있다.

  ● 메모리 버퍼를 잘 잡아서 속도를 보장하자
MSSQL서버는 메모리를 동적으로 잡는다. 물론 시스템 자원이 요청하면 메모리를 클리어시키지만 그렇지 않으면 계속 메모리가 늘어난다. 필자는 1GB 메모리의 시스템에서 980MB의 메모리를 잡고 있는 SQL 서버를 처음 봤을 때 무척이나 놀랐던 기억이 있다. 물론 DB 서버가 독립적으로 운영된다면 동적으로 메모리를 잡는 것이 낫지만, 여러 가지 서버가 한 서버에 연동돼 작동되는 서버라면 고정 메모리를 시스템 성능에 맞게 적절하게 분석해 설정할 경우 서버 속도가 훨씬 안정적일 것이다.

  ● 초기 설정이 시스템 속도에 많은 영향을 미친다
우리는 데이터베이스를 설정할 때 초기 값을 디폴트로 하는 경우가 많다. 필자도 프로젝트에서 데이터베이스를 무심코 디폴트로 생성하곤 했는데, 이렇게 했을 경우 이후 시스템 튜닝이 어려워질 수 있다. 초기에 데이터베이스를 생성할 때 필수적으로 로그와 데이터 크기를 감안해 데이터베이스의 용량을 적절하게 잡아주는 것도 중요하다.

실제 적은 양으로 설정되어 있어도 문제는 없으나 시스템이 자동으로 증가하기 때문에 이러한 시간도 늘어나게 된다. 연결 설정도 마찬가지이다. 제어판에 있는 SQL 서버 정보에서 동시 접속자의 기본 사용자 수인 5명을 적절하게 조절해 SQL의 접속 수를 늘려서 속도를 높여줄 필요가 있다.

● 글자가 많은 부분에 컬럼 속성을 char로 하지 마라
필자는 이 버그(?)를 찾는 데만 며칠이 걸렸다. 실제로 MSSQL에서 char(3000)이라고 설정해 고객의 응답 내용을 담았는데 PHP로 불러오면 256 글자밖에 보이지 않는다. char를 보이게 하려고 노력했으나 php.net에서도 찾지를 못했다.

어떻게든 이 문제를 해결하기 위해 text 필드를 사용했는데 이것 또한 256 밖에 보이지 않았다. 이것은 PHP가 버전업하면서 문제해결을 지원했다. 이를 해결하는 방법은 윈도우의 경우 php.ini에서 다음과 같은 부분을 찾아 필요한 크기만큼 늘려주면 된다.

필드를 사용하고자 한다면, 이런 사항을 유념해 설계하기 바란다. 소스에서 ;(주석)을 제거하는 것을 잊지 말자.

   ; Valid range 0 - 2147483647. Default = 4096.
   ;mssql.textlimit = 4096
   ; Valid range 0 - 2147483647. Default = 4096.
   ;mssql.textsize = 4096


대형 프로젝트에선 PHP와 오라클이 만난다
대형 프로젝트에서는 어김없이 오라클과 만난다. DBA가 아닌 개발자라면 크게 부담 갖지 말고 프로그래밍에 임하면 될 것이다. 오라클은 대형 데이터베이스이기 때문에 설계부터 구현까지 아주 복잡하고, 거대하게 만들어질 때가 많다.

이런 프로젝트는 PHP의 기법을 중심으로 개발에 임하기보다는 크고 복잡하게 구성되어 있는 데이터베이스의 관계와 업무 프로세스를 익히는 것이 더 중요하다. 실제 프로젝트에서는 관련 업무 프로세스를 먼저 알고 가는 것이 이후 수정 및 재개발 업무를 훨씬 줄일 수 있다.

오라클에서도 MySQL이나 MS SQL처럼 쉽게 게시판 리스트를 만들 수 있다. 앞의 게시판 리스트와 똑같은 코드를 오라클 연동 버전으로 변환해 보자.

<?
$conn = OCILogon("scott","tiger");
$stmt = OCIParse($conn,"select * from web_board");
OCIExecute($stmt);

$nrows = OCIFetchStatement($stmt,$results);
if ( $nrows > 0 ) {

           for ( $i = 0; $i < $nrows; $i++ ) {
            reset($results);
            while ( $column = each($results) ) {
              $data = $column['value'];
              $number = $column['number'];
              $name = $column['name'];
              $date = $column['write_date'];
              $title = $column['title'];
              $content = $column['content'];
              $hit = $column['hit'];
            }
           }
} else {
           echo "No data found";
}

OCIFreeStatement($stmt);
OCILogoff($conn);
?>


현재 쓰여 있는 소스 코드는 OCI 함수로 프로그래밍되어 있다. OCILogon() 함수는 첫 번째와 두 번째 인수로 DB 계정의 사용자 이름과 패스워드를 받고, 세 번째 인수로는 로컬 오라클 인스턴스(Local Oracle instane) 이름이나 접속 가능한 tnsnames.ora의 엔트리 이름을 명시한다. OCIParse() 함수로 SQL문의 구문의 유효 여부를 리턴한다. OCIExecute() 함수로 SQL문을 실행한다.

첫 번째 인수는 OCIParse의 리턴 값을 취하고, 두 번째 인수는 커밋 모드에 관한 것이다. 생략가능한 인자이다. 생략했을 때 두 번째 인수의 디폴트 값은 OCI_COMMIT_ON_SUCCESS로 SQL을 실행한 후 바로 커밋한다.

테이블에 변경 작업을 하는 SQL문 이후에 롤백하기 위해서는 두 번째 인수로 OCI_DEFAULT을 지정해야 한다.

물론 표준 오라클 함수로도 프로그래밍할 수 있다. PHP는 오라클 함수를 OCI 함수와 표준 ORA 함수를 지원한다. 다음은 앞의 코드를 ORA 함수로 프로그래밍한 코드이다.

<?
$conn = ora_Logon("TNS", "password");
$curs = ora_open($conn);
$query = sprintf("select * from web_board");

ora_parse($curs, $query);
ora_exec($curs);
ora_fetch($curs);

$ncols = ora_numcols($curs);
$nrows = ora_numrows($curs);

for ($j=0; $j<$nrows; $j++)
           {
              for ($i=0; $i<$ncols; $i++)
              {
                     $col = ora_getcolumn($curs, $i);
              }
           ora_fetch($curs);
           }
?>


ORA 함수로 코딩된 소스를 보면 ora_Logon()으로 로그온하고 데이터베이스를 ora_open()으로 열고, ora_parse()로 구문을 검사한 후 ora_exec(), ora_fetch()를 통해 쿼리를 실행한다. 그리고 나서 ora_numcols()으로 컬럼 개수를 가져온다, 또한 ora_numrows() 함수는 전체 행을 가져오는 함수이다. 이 함수의 결과 값을 이용해 loop를 돌려 해당 컬럼 값을 순차적으로 가져오는 것이다.

개발자는 OCI함수와 ORA 함수 중 자신이 선호하는 함수를 선택해 개발하면 된다. 다른 데이터베이스보다 선택 폭이 넓다는 장점이 있다. 또한 많은 레퍼런스가 제공되기 때문에 개발에 크게 어려움이 없을 것이다. 오라클 또한 국내보다는 국외 레퍼런스가 많기 때문에 외국 사이트를 잘 찾아내는 것도 중요할 것이다.

커밋과 롤백에 대한 이야기
데이터를 조회하거나 변경하는 DML(Data Manipulation Language)에는 SELET, INSERT, UPDATE, DELETE가 있는데, 이러한 구문은 롤백(rollback)이 가능하다. 이중 SELET를 제외한 구문은 모두 데이터베이스에 변경을 가하는 구문인데, 이러한 구문을 사용해 데이터베이스에 변경한 사항을 COMMIT 명령으로 완료하거나 ROLLBACK 명령으로 취소할 수 있다는 얘기이다.

반면, 데이터 구조를 정의하는 DDL(Data Definition Language)에는 CREATE, DROP문이 있다. 이러한 구문이 데이터베이스에 가한 변경 작업은 ROLLBACK 명령으로 취소할 수 없다. 즉 DLL 구문을 실행할 때에는 즉시 데이터베이스에 반영된다. ROLLBACK문은 CREATE TABLE을 통해 작성된 테이블을 제거하지 못하므로 생성된 테이블을 제거하기 위해서는 DROP TABLE문을 사용해야 한다.



오라클 활용하기
  ● 클라이언트 설정 오류를 조심하자
오라클 시스템을 운영할 때 기본적으로 웹 서버 + DB 서버로 분리를 한다. 그렇다면 웹 서버에서 오라클 데이터베이스에 접근해야 한다. 그렇게 하기 위해서는 오라클 클라이언트를 반드시 웹 서버에 설치해야 한다.

필자는 테스트를 위해 오라클 클라이언트를 윈도우 2000에 설치하고 PHP를 설치하고자 하였으나 오라클 연동되지 않아 낭패를 본 경우가 있었다. 결국 PHP 사이트의 버그 리포트를 뒤져보니 오라클 클라이언트 8.16과 PHP의 호환 버그가 문제였다.

시스템 연동을 생각하는 독자들은 반드시 오라클 클라이언트 8.17을 설치하기 바란다. 또한 NetEasyconfig를 한 후에는 리부팅해 호환에 만전을 기하기 바란다.

  ● rownum을 활용하자
MySQL의 limit와 같이 오라클에서도 rownum을 제공한다. 특정 부분만 쿼리를 실행하기 때문에 속도 보정을 할 수 있다.
rownum에 대한 자세한 설명은 생략하도록 하겠다. 오라클 관련 문서에 보면 자세하게 설명되어 있을 것이다.

  ● 서브 쿼리를 잘 활용하자
오라클은 서브 쿼리를 지원한다. 그리하여 SQL을 전문으로 하는 개발자나 DBA를 보면 엄청난 길이의 서브 쿼리를 사용하기도 한다. 복잡한 계산 과정은 서브 쿼리를 사용한 속도를 저하시키는 장문의 SQL문보다 단순한 SQL 여러 개를 실행시키는 것이 PHP에서 빠르지만 속도가 문제되지 않는 부분에서는 서브 쿼리를 잘 이용하는 방법도 개발에 효율적일 것이다.

  ● order by와 인덱스를 적절하게 사용하자
일반적으로 대용량의 데이터를 가진 테이블의 쿼리에서 order by를 권장하지 않는다. 정렬에 의해 속도가 아주 늦어지기 때문이다. 이러한 경우에 가장 기본적으로 생각해 볼 수 있는 방법이 인덱스를 설정하는 방법이다. 인덱스를 설정했다면, order by문을 사용하더라도 기본적인 속도를 보장해 줄 것이다. 인덱스는 데이터 용량이 커질 수 있는 테이블을 설계 단계에서 미리 설정해 놓는 것이 좋으며 재수정 가능하게 설계하도록 하자.

  ● sqlloader를 활용하자
대용량의 데이터를 삽입하거나 처리할 때 인덱스가 있는 테이블이라면 엄청난 로드가 걸린다. 이것은 테이블이 하나를 삽입할 때마다 인덱스를 재설정하기 때문이다. 이럴 때는 아예 인덱스를 드롭하고 삽입이 끝난 후 다시 인덱싱하는 방법이 훨씬 빠르다. 보통 이런 대용량 처리는 매 경우 삽입하는 것보다 파일 데이터를 sqlloader를 이용해 사용하는 방법이 훨씬 도움이 될 것이다.

사이베이스와 PHP 연동하기
사이베이스는 유명한 데이터베이스임에도 불구하고, PHP에서 잘 알려진 정보가 없는 것으로 알고 있다. 필요한 독자가 있을 것이라는 판단에 간략하나마 사이베이스와의 연동을 다루도록 하겠다. 사이베이스 또한 PHP에서 지원하는 함수를 사용하면 다른 데이터베이스와 마찬가지로 쉽게 데이터를 핸들링하고 리스트를 출력할 수 있다.

다음은 앞의 데이터베이스 코드와 동일한 기능을 하는 코드이다. sysbase_connect() 함수로 데이터베이스에 접속하고, sybase_query() 함수로 SQL문을 실행하고, sysbase_fetch_array() 함수로 순차적으로 게시물 데이터를 가져온다.

<?

$cnn = sybase_connect("db_server","sa","password");
$result = sybase_query("select * from web_board");
$i = 0;
while ($r = sybase_fetch_array($result)) {
                     $i++;
                     $data = $column['value'];
                     $number = $column['number'];
                     $name = $column['name'];
                     $date = $column['write_date'];
                     $title = $column['title'];
                     $content = $column['content'];
                     $hit = $column['hit'];

}
?>


조금 눈여겨 본 독자라면 거의 모든 데이터베이스를 핸들링하는 구조가 비슷하다는 것을 느꼈을 것이다. 개발자는 자신이 사용하는 데이터베이스 관련 함수만 잘 습득해도 다른 데이터베이스에 쉽게 적응할 수 있다. 왜냐하면 대부분의 함수 이름과 기능이 비슷하기 때문이다.

사이베이스에서 한 가지 유념할 것은 속도 보장을 위해 sybase_result() 함수를 가급적 사용하지 말기 바란다. 이것은 전체 행을 가져오는 함수이기 때문에 속도가 많이 느릴 수 있다. 이런 이유로 sybase_result() 함수보다는 순차적으로 가져오는 sybase_fetch_object(), sybase_fetch_array(), sybase_fetch_row() 함수들을 권고하고 있다. 실제 커뮤니티를 방문하면 사이베이스에 대한 정보가 많이 적다. 관련 개발자들은 www.php.net의 메일링이나 아카이브를 검색하는 것이 많은 도움이 될 것이다.

속도 개선을 설계로 해결할 수 있을까?
데이터베이스 프로그래밍을 하면서 많은 개발자들의 관심사는 빠른 데이터 로딩이다. 현재 운영되는 사이트를 보면 초기개발 단계에서 설계 오류로 데이터가 10만건을 넘어가기 시작하면 데이터 핸들링에서 엄청난 속도 부하가 나타난다.

대부분 사이트는 초기 개발 비용보다 많은 재개발 및 튜닝 작업 비용을 들여 사이트 개편에 나서는 경우가 허다하다. 가장 일반적인 웹 게시판의 경우도 마찬가지다(필자가 말했듯이 웹 게시판의 로직은 거의 모든 데이터베이스 연동 프로그래밍의 기본이다. 전부라고 말해도 과언이 아님을 유념하길 바란다).

웹 게시판을 어떻게 설계하는 것이 좋을까? 이 부분으로 웹 게시판 설계를 고쳐보도록 하자. 앞 부분에서 다뤘듯이 데이터베이스에서 주 키나 인덱스로 속도와 조회 문제를 다소 해결할 수 있다. 하지만 이것으로는 방대하게 늘어나는 데이터를 빠르게 로딩하기는 힘들다. 현재 게시판의 간단한 프로그래밍 구조를 살펴보자. 게시판은 최근의 10개의 데이터를 먼저 보여주고 페이징을 시도해 페이지를 나눈다. 프로그래밍 구조는 다음과 같다.

     ① 전체의 게시물 개수를 최근 데이터로 정렬해 구한다.
     ② 최근 데이터를 기준으로 10개씩 페이징을 시도한다.
     ③ 최근 10개를 for 루프로 돌려 값을 출력한다.

만일 데이터가 몇 만개를 넘어섰을 때 전체 개수를 가져오는 곳부터 데이터의 로딩이 걸린다. 페이징을 시도하더라도 전체 개수를 가져오는 루틴은 늘 걸리기 때문에 데이터의 로딩 시간은 해결되지 않는다.

가장 쉽게 이 문제를 해결하는 방법은 무엇일까? 제일 간단하게 접근한다면 전체 행을 가지고 있는 테이블을 새로 하나 생성해 사용하는 방법이다(물론 여기서 계층형 게시판이나 아주 정밀한 게시판의 구조에 대해 설명하는 것이 아님을 밝힌다). 전체 카운트를 가지고 있는 테이블이 있는 프로그램의 구조를 살펴보자.

     ① 전체의 게시물 개수를 전체 카운트 테이블에서 조회한다.
     ② 최근 데이터를 기준으로 10개씩 페이징을 시도한다.
     ③ 해당 10개를 for 루프로 돌려서 값을 출력한다.

얼핏 보면 비슷할 것 같지만, 페이징을 시도하기 위해 전체 개수를 가져오는 부분에서 속도가 개선될 것이다. 다음 페이지를 넘겨도 전체 카운트 테이블에서 개수를 조회하기 때문에 빠르게 가져올 수 있다. 물론 이 구조는 완벽하지 않다.

답변 글에 대한 계층 설계도 없고, DELETE시 카운트가 중간에 빠졌을 때 전체 카운트는 어떻게 처리할 것인지, 페이징은 어떻게 할 것인지 등을 고려해야 한다. 여기서 필자가 말하고 싶은 것은 확장성을 고려해 기초 설계를 하자는 것이다. 작은 아이디어가 속도 개선에 엄청난 도움을 줄 것이다.

실제로 대량 데이터에 대한 게시판 처리 기법이나 페이징 기법은 많은 개발자의 관심사였으며, 커뮤니티의 기술 토론을 이끌어 가는 주축이기도 했다. 이런 정보는 커뮤니티에 많이 제시되어 있으며 그것을 참고하면 될 것이다. 다만 이 글에서는 이런 방법을 시도하는 개발자가 속도 개선과 개발 업무 개선을 이끌어 나갈 수 있다는 것을 말해주고 싶어서다.

자신감과 경험이 곧 지식
이번 호에서는 각종 데이터베이스에 대한 간단한 프로그래밍 소개와 이용시 도움이 되는 방법들을 다루었다. 데이터베이스는 앞에서도 밝혔듯이 웹 프로그래밍의 가장 근간이라고 할 수 있다. 웹 프로그래머에게 요구되는 다양한 데이터베이스 처리에 대한 방법, 이러한 데이터베이스를 빠르고 안전하게 구현하는 방법을 다양한 경험으로 해결하기 바란다.

불과 몇 년 전만 해도 데이터베이스 없이 운영되는 사이트도 많았는데, 현재 그런 사이트는 하나도 찾을 수 없을 정도로 우리 곁에 데이터베이스가 있다. 이러한 데이터베이스에 대한 스킬은 다양한 경험과 학습에서 나오는 것임을 다시 한번 당부하고 싶다.

또한 개발자로서 자신 있는 데이터베이스를 반드시 하나쯤 갖추라고 말하고 싶다. 하나의 데이터베이스에 정통하게 되면, 다른 데이터베이스를 접할 때는 비교할 줄 알고, 테스트할 줄 알고, 응용할 줄 아는 새로운 스킬이 개발자에게 부여 될 것이다.

 

출처 : http://cysnim12.tistory.com/entry

+ Recent posts