admin post

이 글은 PHP 외의 다른 프로그래밍 언어에 익숙한 사람을 대상으로 PHP를 빠르게 시작할 수 있게 할 목적으로 쓴 글이다. 예전에 PHP로 진행하던 프로젝트를 인수인계할 일이 있었는데, 인수인계를 받는 사람이 C언어는 익숙하지만, PHP는 거의 써 본 일이 없어서, 인수인계용으로 쓴 글을 다듬어서 여기에 공개한다. 주된 내용은 PHP의 기본적인 규칙과 다른 언어와 달라서 주의해야 할 점으로 이루어져 있다. 소제목 같은 건 없고 순서대로 쭉 읽으면 된다.

소스코드에서 PHP 실행 코드는 <?php 와 ?> 사이에 있는 코드만 실행되며, 밖에 있는 문자는 그냥 출력된다.
문자열 출력은 <?php와 ?> 사이에서 echo "hello, world!"; 를 사용하거나, 밖에서 <?= "hello, world!" ?> 형식으로 출력할 수 있다. 그래서 보통 이런 구조를 가진다.

<?php
// 서버 내부 로직 수행 (DB 입출력, 사용자 입력 처리)
?>
<!DOCTYPE html>
<html>
...
<p><?= "hello, world!" ?></p>
<p><?php echo "hello, world!"; ?></p>
...
</html>

에러 메시지는 아파치 서버의 에러로그를 보면 된다. (/var/log/apache2/error.log (우분투 리눅스 기준))
터미널에서 tail -f /var/log/apache2/error.log 라고 실행하면 실시간으로 에러로그를 볼 수 있다.
(php 설정에 따라 실행화면(웹 브라우저로 보는 화면)에 에러 메시지를 바로 출력하는 경우도 있다. 또한, PHP 버전에 따라 기본 설정값이 다르기도 한다)
(만약 서버 로그를 볼 수 없는 웹 호스팅 서비스라면 실행화면에 에러메시지가 출력될 가능성이 높다)

변수명은 앞에 $가 붙으며 대소문자를 구분한다.
함수명과 클래스명은 대소문자를 구분하지 않는다. (무슨 말이냐 하면, myFunc()라고 선언하고 myfunc()라고 호출 가능하다)

변수는 미리 선언하지 않으며, 값을 할당할 때 생성된다. 변수 타입도 미리 정하지 않는다. 변수가 생성된 적이 있는지 확인하려면 isset()이나 empty()를 사용하면 된다.

if(!myFunc()) $errorMessage = "something wrong!";
...
if(isset($errorMessage)) echo $errorMessage;
if(!empty($errorMessage)) echo $errorMessage;

empty()는 이미 생성된 변수라도 값이 비어있으면 TRUE를 리턴한다.
isset()이나 empty()가 아닌 함수에, 생성된 적이 없는 변수명을 파라미터로 주면 warning이 뜬다.

숫자와 문자열을 같이 연산하면 문자열이 숫자로 캐스팅된다.
1 + "100" 은 101
2 * "30" 은 60
"01234", "124asdf", "asdf"를 숫자로 캐스팅하면 각각 1234, 124, 0 이 된다.

문자열을 합칠 때는 . 연산자를 사용한다. 이 때는 숫자도 문자열로 캐스팅된다.
"as" . "df" 는 "asdf"
"as" . 123 은 "as123"

문자열은 큰 따옴표나 작은 따옴표로 묶어서 표현할 수 있다. (C와는 다르게 char 형은 없다.)
"asdf"
'asdf'

문자열 안에서 줄바꿈이 가능하다.
"Hello
World"

큰 따옴표 안에서 \n, \r, \t 등의 특수문자를 쓸 수 있다. 작은 따옴표 안에서는 특수문자로 바뀌지 않고 적은 그대로 남아있는다.

echo "Hello\nWorld";
// Hello
// World

echo 'Hello\nWorld';
// Hello\nWorld

큰 따옴표 안에 변수를 넣을 수 있다.

$a = 3;
echo "I have $a cows."; // I have 3 cows.
echo "{$a}GB"; // 3GB

클래스 오브젝트의 멤버에 접근할 때는 -> 연산자를 사용한다.

$obj1->member1

클래스의 statc 멤버에 접근할 때는 :: 연산자를 사용한다.

ClassName::static_member1

두 값의 같음을 판단하는 연산자는 == 와 === 두 개가 있다.
== 는 값이 같으면 TRUE, === 는 값과 타입이 모두 같으면 TRUE

0 == "asfd" 는 TRUE 이다. (문자열이 숫자로 캐스팅)
0 == FALSE 는 TRUE 이다. (0이 boolean으로 캐스팅)
그런데 "asdf" == FALSE 는 FALSE 이다. (""를 제외한 문자열은 boolean으로 캐스팅하면 TRUE 값을 가진다)
즉, PHP의 == 연산자는 A == B이고 B == C라도 A == C를 보장하지 않는다.

캐스팅에 대한 위험이 우려된다면 === 를 사용하는게 좋다.(특히, array나 문자열의 인덱스를 리턴하는 함수가 0을 리턴할 수도 있고 FALSE를 리턴할 수도 있는 경우. strpos(), array_search())

두 값이 다름을 판단하는 연산자는 !=와 !== 이다. 전자는 값이 다르면 TRUE, 후자는 타입이 다르거나 값이 다르면 TRUE

switch case 문은 내부적으로 if else로 변한되어 실행된다. 그래서 다음과 같은 상황을 주의해야 한다.

$var1 = "rm -rf /";
switch($var1) {
	case 0:
		echo "0";
		break;
	case 1:
		echo "1";
		break;
	default:
		echo "error: invalid value!";
		break;
}

위의 코드를 실행하면 default 항목의 에러 메시지가 출력되지 않고, case 0의 0이 출력된다. 위의 코드를 if else로 변환하면 다음과 같다.

$var1 = "rm -rf /";
if($var1 == 0) {
	echo "0";
} else if ($var1 == 1) {
	echo "1";
} else {
	echo "error: invalid value!";
}

if($val1 == 0) 에서 이미 TRUE 이므로 뒤쪽의 case나 default는 값이 같은지 평가조차 하지 않는다.

echo TRUE; 는 화면에 1을 출력한다.
echo FALSE;는 화면에 아무것도 출력하지 않는다.
정확한 값을 출력해보고 싶으면 var_dump()나 print_r()함수를 사용하면 된다.

array는 $array1 = array() 형식으로 생성한다.
array의 인덱스는 숫자가 될 수도 있고 문자열이 될 수도 있다. 인덱스라기 보다는 key - value 쌍으로 된 구조라고 생각하면 된다. (Python dictionary, JSON object, C++ STL map 등을 생각하면 된다)

$array1 = array();
$array1["jon"] = "jal";
$array1[0] = 3.14159

value의 타입을 섞어 써도 된다. array를 C의 array처럼 쓰러면 key를 0부터 n까지 빠짐없이 숫자로만 구성하면 된다.

array의 key - value 값을 미리 정해줄 수도 있다.

$array2 = array("hi" => "hello", "ho" => "holo");
$array3 = array("a", "b", "c", "d");	// C의 array 처럼 사용할 경우 이렇게 사용. 이 경우 인덱스는 0부터 3까지 생긴다.

(참고로 => 는 비교연산자(같거나 크다)가 아니다)

for 루프를 통해 array의 각 항목에 접근하는것에는 3가지 유형이 있다.

foreach($array1 as $value) {
	echo $value;
}
foreach($array2 as $key => $value) {
	echo $key . ": " . $value;
}
for($i = 0, $loops = count($array3); $i < $loops; $i++) {
	echo $array3[$i];
}

array의 key가 정의되어 있는지 확인하기 위해서는 isset()을 사용하면 된다. (또는 empty()를 써도 된다.)

if(isset($array1["errorMessage"])) echo $array1["errorMessage"];

상수의 정의는 define() 을 사용한다.

if(!defined("MY_CONSTANT")) define("MY_CONSTANT", 10392);

정의한 상수를 사용할 때는 따옴표 없이 사용한다.

$var1 = MY_CONSTANT;

함수 안에서 전역 변수에 접근할 때는 $GLOBALS 를 사용한다.

$var1 = 3;
function myFunc() {
	$g_var1 = $GLOBALS["var1"];
}

변수의 레퍼런스는 참조할 변수 앞에 &를 붙여서 새로운 변수로 어싸인하면 된다.

$var1 = &$array1["jon"];
$var1 = "sam";	// $array1["jon"]의 값이 "sam" 이 된다.

레퍼런스를 끊으려면 unset()을 사용한다.

unset($var1);

call by reference는 함수 선언시 파라미터 앞에 &를 붙인다.

function myFunc(&$var1) {
	$val1[0] = "overwrite the original data";
}
$myVar = array("big big size data");
myFunc($myVar);

return by reference는 함수 선언시 함수 이름 앞에 &를 붙인다.

function &myFunc() {
	static $var1 = "hello";
	return $var1;
}
$myVar = &myFunc();

함수 선언과 호출부 모두 &를 붙여야 한다. 호출부에서 &를 붙이지 않으면 최종적으로는 값이 복사된다.

런타임에 다른 소스파일을 현재 코드에 불러오고 싶을 때는 require("source_file_path"); 형식으로 사용한다.
여러번 호출되더라도 딱 한 번만 불러와야 되는 상황이라면 require_once("source_file_path"); 형식으로 사용한다.
클래스나 함수가 선언된 파일을 불러올 때는 require_once()를 사용하면 된다.
(require()/require_once() 대신 include()/include_once() 가 있는데 특별한 경우를 제외하고는 사용하지 않기를 추천한다. require()는 파일 불러오기를 실패하면 실패한 즉시 실행이 멈추지만, include()는 불러오기를 실패해도 다음 코드를 계속 실행한다.)

미리 정의된 특수한 전역 변수가 있다.
$_GET, $_POST, $_FILES, $_SERVER, $_COOKIE, $_SESSION
이 변수들은 특정 함수 호출이나 인스턴스 생성의 절차 없이, 코드의 시작 부분부터 값이 정해져 있다. 그냥 바로 쓰면 된다.
또한, 이들은 함수 안에서도 $GLOBALS를 통하지 않고 변수 이름만으로 바로 접근이 가능하다.

$_GET은 URL에 추가된 파라메터의 값을 자동으로 가져온다.
http://example.com/a.php?b=1&c=2&d=asdf
라는 URL로 접근하면 a.php에서 $_GET 값은 array("b" => "1", "c" => "2", "d" => "asdf")의 값을 가진다.

$_POST는 $_GET과 유사한 형식으로 form으로 전송한 값을 가진다. html에서

<form method="post" action="a.php">
<input type="text" name="b" value="1" />
<input type="text" name="c" value="2" />
<input type="text" name="d" value="asdf" />
<input type="submit" />
</form>

의 코드를 작성하고 submit 버튼을 누르면 a.php에 $_POST 값이 설정된다.

$_FILES는 파일 업로드시 업로드한 파일 정보가 들어가있다.
$_COOKIE 는 쿠키 값들이 array 형태로 들어있다.
$_SERVER는 사용자의 IP 주소, 웹 브라우저 정보 등을 포함해서 잡다한 정보가 들어있다.
print_r() 함수로 각각의 값을 출력해보면 어떻게 써먹을 지 감이 잡힐 것이다.

PHP의 기본 라이브러리 함수들 중에 C의 표준 라이브러리와 같은 이름의 함수들이 많다. 기능도 거의 같다. 하지만 파라미터 순서나 리턴값이 조금씩 다른 경우가 있으므로 php.net 에서 한 번씩 검색해보고 쓰자. 예: sprintf
또한, C에서 처럼 함수 리턴값으로 에러 발생 여부를 알려주기 때문에 try catch를 쓸 일이 별로 없다.

여담으로, if, for 등에서 실행블럭을 지정할 때 중괄호를 쓰지 않고 콜론과 endif, endfor 등의 키워드를 쓰는 방법이 있다.

if(myFunc()):
// do songthing
endif;

실행블럭이 커지거나 if, for 등이 중첩되면 어디서 열고 닫는지 헷갈리므로 웬만하면 쓰지 말자.

'그저' 카테고리의 다른 글

전화요금 기록 다시 갱신  (1) 2015.03.15
월 전화요금 최저 기록 갱신  (1) 2014.01.13
멜론북스 다운로드 구매 후기  (0) 2013.05.07
모에적성검사 2009년 버전 결과  (2) 2009.01.28
YOUR COMMENT IS THE CRITICAL SUCCESS FACTOR FOR THE QUALITY OF BLOG POST