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 등이 중첩되면 어디서 열고 닫는지 헷갈리므로 웬만하면 쓰지 말자.

YOUR COMMENT IS THE CRITICAL SUCCESS FACTOR FOR THE QUALITY OF BLOG POST
  1. Favicon of http://struggle.tistory.com/ BlogIcon 정상택 2013.10.30 13:09 신고  댓글주소  수정/삭제  댓글쓰기

    설명 잘 되어 있네.

  2. 듣보잡 2015.07.30 10:09 신고  댓글주소  수정/삭제  댓글쓰기

    굿