PHP Best Practices

일반적이고 복잡한 PHP 작업을 위한 짧고 실용적인 가이드

PHP Best Practices

최신 개정판 & 관리자

이 문서는 2016년 6월 21일에 최종 검수 및 수정되었습니다.

이 문서는 이 관리하고 있습니다. 오랜 기간 PHP를 작성해왔고, 현재 진지한 작가를 위한 온라인 글쓰기 그룹인 Scribophile, 프리랜서를 위한 쉬운 온라인 글쓰기 포트폴리오인 Writerfolio, 진정한 애서가들을 위한 무료 eBook을 만드는 오픈소스 프로젝트 Standard Ebooks 등을 운영하고 있습니다.

뭔가 도울 일이 있거나 이 문서에 어떤 제안이나 정정할 것이 있다면 연락주세요.

한국어판 안내

이 문서는 2016년 6월 21일에 수정된 내용을 기반으로 2017년 2월 22일에 최종 번역되었습니다. 한국어판에 대한 제안/문의는 Github 저장소에 이슈로 남겨주세요.

위로 이동

소개

PHP는 수년간 비틀리고, 휘어지고, 늘려지고, 난도질 당해온 복잡한 언어입니다. PHP는 매우 일관성이 없고 가끔 결함도 있습니다. 각 버전마다 각각의 독특한 기능과 결점, 특성을 가지고 있어서, 어떤 버전에 어떤 문제가 있는지 계속 확인해나가는 것은 쉽지 않습니다. 항상 PHP가 왜 그렇게 미움을 받는지 아는 것은 어렵지 않습니다.

그럼에도 불구하고, PHP는 오늘날 웹에서 가장 인기있는 언어입니다. PHP의 긴 역사로 인해서 비밀번호 해싱hashing이나 데이터베이스 사용과 같은 기본적인 것을 어떻게 하는지에 관한 많은 학습서tutorials를 찾을 수 있습니다. 문제는 5개의 문서를 본다면 무언가를 하기위한 완전히 다른 다섯가지 방법을 발견하게 될 가능성이 높다는 것입니다. 어떤 방법이 "옳은" 방법일까요? 그 외의 다른 방법들은 잠재적인 버그나 문제gotchas가 있는 건가요? 이런 것을 알아내기는 정말 어렵습니다. 여러분은 정답을 알아내기 위해서 인터넷을 찾아볼겁니다.

이런 다양한 방법들의 존재는 종종 새로운 PHP 프로그래머들이 보기 흉하고, 구식이며, 안전하지 않은 코드를 만들게 되어 비난당하게 되는 이유 중 하나입니다. 하지만 첫번째 구글 검색 결과가 5년된 방법을 가르치는 4년된 문서라면 어쩔 수 없는 일이지요.

이 문서는 이런 문제를 해결하고자 합니다. PHP에서 일반적이고 복잡한 문제, 작업을 위한 모범 사례best practices로 꼽을 수 있는 기본적인 설명들을 한데 모아보고자 합니다. 낮은 수준의 작업인데 PHP에서 다양하고 복잡한 해결방법들이 존재한다면, 여기에 해당합니다.

이 문서가 포함하는 것

이 문서는 PHP 프로그래머가 직면할만한 일반적인 낮은 수준low level의 작업이지만, PHP가 많은 선택권을 제공하기 때문에 어떻게 할지 불명확한 작업을 만났을때 택해야할 가장 바람직한 방법을 제안하는 안내서입니다. 예를 들자면, 데이터베이스 연결하기는 PHP에서 많은 방법으로 가능한 일반적인 작업이지만, 그 모두가 좋은 방법은 아닙니다. 따라서, 이것은 이 문서에 포함됩니다.

이 문서는 해결 방법들을 짧게 소개하고 있습니다. 예제는 여러분을 기본적인 설정을 알려주겠지만, 여러분은 나름대로 관련 자료를 찾아서 유용한 것들로 살을 붙여야 합니다.

이 문서는 최신 PHP를 사용하는 것을 가정합니다. 그래서 여러분이 오래된 버전의 PHP를 사용한다면 이러한 해결 방법들에서 사용하는 어떤 기능들을 사용하지 못할지도 모릅니다.

이 문서는 살아있는 문서로 PHP가 발전하는 것에 맞춰서 이 문서도 계속 수정하도록 최선을 다할 예정입니다.

이 문서가 포함하지 않는 것

이 문서는 PHP 학습서가 아닙니다. PHP의 기본적인 내용과 문법 등은 다른 곳에서 배우셔야 합니다.

이 문서는 쿠키 저장이나 캐시caching, 코딩 스타일, 문서화 등과 같은 일반적인 웹 애플리케이션 문제를 위한 안내서가 아닙니다.

이 문서는 보안 안내서가 아닙니다. 보안 관련 문제를 조금 다루고 있긴 하지만, 여러분의 PHP 애플리케이션을 안전하게 만들기 위해서는 별도로 관련자료를 찾아보셔야 합니다. 특히, 여기에서 제안한 방법을 구현하기 전에 신중하게 검토하세요. 여러분의 코드는 여러분 자신의 책임입니다.

이 문서는 특정 코딩 스타일이나 패턴, 프레임워크를 주장하지 않습니다.

이 문서는 사용자 등록, 로그인 시스템 등과 같은 높은 수준의 작업을 하기 위한 특정 방법을 옹호하지 않습니다. 이 문서는 철처히 긴 PHP의 역사로 인해서 해결 방법이 복잡하고 불명확할 수 있는 낮은 수준의 작업을 위한 문서입니다.

이 문서에 소개된 방법이 가장 중요하다거나 단 하나의 방법인 것은 아닙니다. 소개된 어떤 방법들은 여러분의 상황에서는 최선이 아닐 수도 있고, 동일한 결과를 얻기위한 다른 많은 방법이 있을 수 있습니다. 특히 로드가 심한high-load 웹 애플리케이션들은 이런 문제를 해결하는 더 난해한 방법이 도움이 될 수도 있습니다.

위로 이동

어떤 PHP 버전을 사용하나요?

Ubuntu 14.04 LTS에 설치된 PHP 5.5.9-1ubuntu4.2

PHP는 웹 세계의 100살 먹은 거북이입니다. 이 거북이의 등껍질에는 풍성하고, 복잡하며, 굴곡진 역사가 새겨져있습니다. 호스팅 환경에서는 여러분이 할 수 있는 것에 제약이 있을 수도 있습니다.

온전함을 유지하기 위해서 하나의 PHP 버전에 집중할 것입니다. PHP 5.5.9-1ubuntu4.2가 그렇습니다. 이것은 Ubuntu 14.04 LTS 서버에서 apt-get을 사용할때 설치되는 버전입니다. 다시 말하면, 많은 사람들이 사용하는 온전한 초기값입니다.

여기에 소개하는 어떤 해결방법은 다른 버전이나 오래된 버전의 PHP에서 잘 동작하는 것을 발견할지도 모릅니다. 그런 경우, 오래된 버전에 숨어있는 버그나 보안 문제들을 확인하는 것은 여러분의 몫입니다.

위로 이동

비밀번호 저장하기

비밀번호를 해시하고 비교할때 내장built-in 비밀번호 해싱 함수를 사용하세요.

해싱은 데이터베이스에 저장하기 전에 사용자의 비밀번호를 보호하는 관례적인 방법입니다. md5나 심지어 sha1과 같은 많은 일반적인 해싱 알고리즘은 해커들이 쉽게 비밀번호를 알아낼 수 있기 때문에 비밀번호를 저장하기에는 안전하지 않습니다.

PHP는 현재 비밀번호 해싱을 위한 최고의 알고리즘으로 여기는 bcript 알고리즘을 사용하는 내장 비밀번호 해싱 라이브러리를 제공합니다.

예제

<?php // 비밀번호를 해시합니다. $hashedPassword 는 60자 문자열이 됩니다. $hashedPassword = password_hash('my super cool password', PASSWORD_DEFAULT); // 이제 $hashedPassword 내용을 안전하게 데이터베이스에 저장할 수 있습니다. // 사용자가 입력한 것과 해시를 비교하여 올바른 비밀번호를 입력했는지 확인합니다. password_verify('the wrong password', $hashedPassword); // false password_verify('my super cool password', $hashedPassword); // true ?>

참고

  • 많은 사람들이 비밀번호를 해싱하기 전에 salt값을 추가하라고(소금을 치라고) 권장할 것입니다. 그것은 좋은 생각인데, password_hash()는 이미 salt값을 추가해줍니다. 여러분이 직접 salt값을 추가할 필요가 없다는 말입니다.

더 읽을거리

위로 이동

MySQL 데이터베이스에 연결하고 쿼리하기

PDO와 Prepared Statement 기능을 사용하세요.

PHP로 MySQL 데이터베이스에 연결하는 방법은 많습니다. PDO (PHP Data Objects)는 최신이면서 강력한 방법입니다. PDO는 여러 종류의 데이터베이스에 객체지향 접근법을 사용하여 일관된 인터페이스를 가지며, 새로나온 데이터베이스들의 더 많은 기능도 지원합니다.

SQL 인젝션 공격을 방지하기 위해서 PDO의 Prepared Statement 기능을 사용해야합니다. bindValue() 함수를 사용하여 여러분의 SQL이 일차적인 SQL 인젝션 공격에서 안전하도록 하세요. (이것이 100% 안전한 것을 보장하지는 않습니다. 이에 관해서는 더 읽을거리를 참고하세요.) 과거에는 "magic quote" 함수들을 조합하여 위험을 방지해야했지만, PDO가 그 모든 지저분한 일을 할 필요없게 했습니다.

예제

<?php try{ // Create a new connection. // You'll probably want to replace hostname with localhost in the first parameter. // Note how we declare the charset to be utf8mb4. This alerts the connection that we'll be passing UTF-8 data. This may not be required depending on your configuration, but it'll save you headaches down the road if you're trying to store Unicode strings in your database. See "Gotchas". // The PDO options we pass do the following: // \PDO::ATTR_ERRMODE enables exceptions for errors. This is optional but can be handy. // \PDO::ATTR_PERSISTENT disables persistent connections, which can cause concurrency issues in certain cases. See "Gotchas". $link = new \PDO( 'mysql:host=your-hostname;dbname=your-db;charset=utf8mb4', 'your-username', 'your-password', array( \PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION, \PDO::ATTR_PERSISTENT => false ) ); $handle = $link->prepare('select Username from Users where UserId = ? or Username = ? limit ?'); // PHP bug: if you don't specify PDO::PARAM_INT, PDO may enclose the argument in quotes. This can mess up some MySQL queries that don't expect integers to be quoted. // See: https://bugs.php.net/bug.php?id=44639 // If you're not sure whether the value you're passing is an integer, use the is_int() function. // (This bug was fixed in Oct. 2016, but the fix is not applied to the version of PHP used in this document; see https://bugs.php.net/bug.php?id=73234) $handle->bindValue(1, 100, PDO::PARAM_INT); $handle->bindValue(2, 'Bilbo Baggins'); $handle->bindValue(3, 5, PDO::PARAM_INT); $handle->execute(); // Using the fetchAll() method might be too resource-heavy if you're selecting a truly massive amount of rows. // If that's the case, you can use the fetch() method and loop through each result row one by one. // You can also return arrays and other things instead of objects. See the PDO documentation for details. $result = $handle->fetchAll(\PDO::FETCH_OBJ); foreach($result as $row){ print($row->Username); } } catch(\PDOException $ex){ print($ex->getMessage()); } ?>

참고

  • 정수형 변수를 바인딩할때 PDO::PARAM_INT 매개변수를 전달하지 않으면 PDO가 종종 따옴표를 추가합니다. 이것은 특정한 MySQL 쿼리를 엉망으로 만들 수 있습니다. 이 버그 리포트를 참고하세요.
  • 연결 문자열에 캐릭터 셋을 utf8mb4로 지정하지 않으면, 설정에 따라서 데이터베이스에 유니코드 데이터가 잘못 저장될 수 있습니다.
  • 캐릭터 셋을 utf8mb4로 선언했더라도, 실제 데이터베이스 테이블이 utf8mb4 캐릭터 셋으로 되어있는지 확인해야합니다. utf8이 아니라 utf8mb4를 사용하는 이유에 대해서는 PHP와 UTF-8 항목을 참고하세요.
  • Persistent 연결을 활성화하면 이상한 동시성 문제가 발생할 수 있습니다. 이것은 PHP 문제가 아니고, 애플리케이션 단의 문제입니다. Persistent 연결은 결과를 잘 알고 사용해야 안전합니다. 이 스택오버플로우 질문을 참고하세요.
  • 하나의 execute() 호출에 한개 이상의 SQL문을 실행할 수 있습니다. 그저 세미콜론으로 문장 사이를 구분해주세요. Ubuntu 12.04 (PHP <= 5.3.10)에서는 이렇게 할 경우 이 버그를 조심하세요. Ubuntu 12.04에 제공되는 PHP에서는 고쳐지지 않았습니다.

더 읽을거리

위로 이동

PHP 태그

<?php ?>를 사용하세요.

PHP 구역을 정하는 방법은 다음과 같은 몇가지 방법이 있습니다. <?php ?>, <?= ?>, <? ?>, <% %>. 짧은 것이 입력하기에는 더 편리하겠지만, 이 태그들은 비활성화되어있는 것이 기본값이고 PHP 서버에 short_open_tag 옵션을 설정해서 활성화시켜야 사용할 수 있습니다. 모든 PHP 서버에서 동작하는 것을 보장하는 것은 <?php ?> 뿐입니다. PHP 코드를 여러분이 통제할 수 없는 서버에 배포할 계획이라면, 반드시 <?php ?>를 사용하세요.

다행히도 <?= 태그는 짧은 태그 활성화 설정 여부와 관계없이 사용가능하므로, 언제든지 안심하고 <?php echo() ?> 와 같은 코드 대신에 짧은 태그를 사용할 수 있습니다.

여러분을 위해서 코딩하면서 사용하는 PHP 설정을 통제할 수 있다면, 짧은 태그가 편리할 수 있습니다. 하지만 <? ?>는 XML 선언과 겹치고, <% %>는 사실 ASP 스타일입니다.

무엇을 선택하든지, 한가지로 일관되게 사용하세요!

참고

  • 순수한 PHP 파일(클래스 정의만 담은 파일 등)에서 ?> 태그와 같은 닫는 태그를 사용할때, 태그 뒤에 빈줄이 들어가지 않도록 하세요.. PHP 파서가 닫는 태그 뒤의 하나의 줄바꿈 문자를 안전하게 "먹어버리지만", 남은 줄바꿈 문자들은 브라우저에 출력될 수 있고, HTTP 헤더를 나중에 출력할 경우에 문제가 될 수 있습니다.
  • 옛 버전의 IE를 대상으로 한다면, ?> 태그와 HTML <!doctype> 태그 사이에 빈 줄이 들어가지 않도록 해야합니다. doctype 선언 전에 줄바꿈과 같은 공백이 나오면, 옛 버전의 IE는 쿼크 모드로 동작합니다. 최근 버전의 IE와 다른 브라우저에서는 문제가 되지 않습니다.

더 읽을거리

위로 이동

클래스 오토로딩(Auto-loading)

spl_autoload_register()를 사용해서 오토로딩 함수auto-load function를 등록하세요.

PHP는 아직 읽어오지 않은 클래스를 포한한 파일을 자동으로 불러오는 몇가지 방법을 제공합니다. 오래된 방법은 매직 전역 함수magic global function__autoload()를 사용하는 것입니다. 하지만 한번에 하나의 __autoload() 함수만을 정의할 수 있어서 __autoload() 함수를 사용하는 라이브러리를 포함시키면, 충돌을 일으키게 됩니다.

이런 문제를 해결하는 올바른 방법은 오토로딩 함수를 뭔가 고유한 이름으로 만들고, 이것을 spl_autoload_register() 함수를 사용하여 등록하는 것입니다. 이 함수는 하나 이상의 __autoload() 함수를 정의하는 것을 허용하기 때문에, 다른 코드의 __autoload() 함수에 문제를 일으킬 우려가 없습니다.

예제

<?php // First, define your auto-load function. function MyAutoload($className){ include_once($className . '.php'); } // Next, register it with PHP. spl_autoload_register('MyAutoload'); // Try it out! // Since we haven't included a file defining the MyClass object, our auto-loader will kick in and include MyClass.php. // For this example, assume the MyClass class is defined in the MyClass.php file. $var = new MyClass(); ?>

더 읽을거리

위로 이동

성능 관점에서의 작은 따옴표와 큰 따옴표

사실 별 상관없습니다.

문자열을 정의할때 작은 따옴표(')를 사용할지, 큰 따옴표(")를 사용할지에 대해 많은 잉크가 쓰였습니다. 작은 따옴표로 묶인 문자열은 해석되지 않아서 문자열에 적힌 그대로 표시됩니다. 큰 따옴표로 묶인 문자열은 해석되어 문자열 내에 있는 PHP 변수들은 모두 평가evaluated됩니다. 줄바꿈을 위한 \n이나 탭을 위한 \t와 같은 이스케이프 문자는 작은 따옴표로 묶인 문자열에서는 평가되지 않지만, 큰 따옴표로 묶인 문자열에서는 평가됩니다.

큰 따옴표로 묶인 문자열은 실행 시간에 평가되기 때문에, 이론적으로는 작은 따옴표로 묶인 문자열은 평가할 필요가 없어서 성능을 향상시킨다고 합니다. 어느 정도의 규모에서는 사실일지도 모르지만, 평범한 현실적인 애플리케이션에서의 차이는 아무 의미 없을 정도로 작습니다. 그래서 보통의 애플리케이션에서는 무엇을 사용하든지 상관없습니다. 극단적으로 부하가 심한 애플리케이션에서는 영향이 약간 있을 수도 있습니다. 여러분의 애플리케이션의 필요에 맞게 선택하시고, 어떤 것을 선택하든지 일관되게 사용하세요.

더 읽을거리

위로 이동

define()const

클래스 상수나 가독성, 세부 최적화micro-optimization를 고려하는 경우가 아니라면, define() 을 사용하세요.

PHP에서는 통상적으로 define() 함수를 사용하여 상수를 정의했습니다. 하지만 어느 시점에 PHP에는 const 키워드를 사용하여 상수를 정의하는 기능이 추가되었습니다. 상수를 정의할 때에 어떤 방법을 선택해야할까요?

정답은 두 방법의 작은 차이에 있습니다.

  1. define()은 상수를 실행 시간에 정의하지만, const는 컴파일 시간에 정의합니다. 이렇게 해서 const가 아주 조금은 빠르겠지만, 큰 규모의 소프트웨어를 만드는 것이 아니라면 아무도 신경쓰지 않을 정도입니다.
  2. 상수 이름에 네임스페이스namespaces를 포함시킬 수도 있겠지만, define()은 상수를 전역 상수로 만듭니다. 즉, define()을 사용해서 클래스 상수를 만드는 것은 불가능합니다.
  3. define()은 상수 이름과 상수값 모두에 표현식expressions을 사용할 수 있지만, const는 모두 사용할 수 없습니다. 이 때문에 define()이 훨씬 유연합니다.
  4. define()if() 블록 안에 있을 수 있지만, const는 불가능합니다.

예제

<?php // Let's see how the two methods treat namespaces namespace MiddleEarth\Creatures\Dwarves; const GIMLI_ID = 1; define('MiddleEarth\Creatures\Elves\LEGOLAS_ID', 2); echo(\MiddleEarth\Creatures\Dwarves\GIMLI_ID); // 1 echo(\MiddleEarth\Creatures\Elves\LEGOLAS_ID); // 2; note that we used define(), but the namespace is still recognized // Now let's declare some bit-shifted constants representing ways to enter Mordor. define('TRANSPORT_METHOD_SNEAKING', 1 << 0); // OK! const TRANSPORT_METHOD_WALKING = 1 << 1; // Compile error! const can't use expressions as values // Next, conditional constants. define('HOBBITS_FRODO_ID', 1); if($isGoingToMordor){ define('TRANSPORT_METHOD', TRANSPORT_METHOD_SNEAKING); // OK! const PARTY_LEADER_ID = HOBBITS_FRODO_ID // Compile error: const can't be used in an if block } // Finally, class constants class OneRing{ const MELTING_POINT_CELSIUS = 1000000; // OK! define('MELTING_POINT_ELVISH_DEGREES', 200); // Compile error: can't use define() within a class } ?>

define()은 매우 유연하기 때문에, 특별히 클래스 상수가 필요한 경우가 아니라면 골치아픈 문제를 피하기 위해서 define()을 사용하는게 좋습니다. const는 보통 읽기좋은 코드를 만들기는 하지만, 대신에 유연성을 포기해야합니다.

어느 쪽은 선택하든지 일관되게 사용하세요!

더 읽을거리

위로 이동

PHP opcode 캐시

운 좋게도, PHP는 내장 opcode 캐시가 있습니다!

옛 버전의 PHP에서는 스크립트를 매번 실행할때마다 이전에 컴파일한 적이 있다고 해도 처음부터 컴파일해야만 했습니다. Opcode 캐시는 이전에 컴파일된 PHP 코드를 저장하여 속도를 약간 높여주는 부가 소프트웨어였습니다. 골라서 사용할 수 있는 여러 종류의 캐시가 있었습니다.

운 좋게도, Ubuntu 14.04에 포함된 버전의 PHP에는 내장 opcode 캐시가 내장되어 기본적으로 활성화되어 있습니다. 그래서 아무것도 할 필요가 없습니다!

캐시가 내장되지 않은 Ubuntu 12.04에서 opcode 캐시를 사용하기 위한 방법은 기록을 위해서 아래에 제공합니다.

더 읽을거리

위로 이동

PHP와 Memcached

분산형 캐시가 필요하다면, Memcached 클라이언트 라이브러리를 사용하세요. 아니면, APCu를 사용하세요.

캐싱 시스템은 애플리케이션의 성능을 향상시킬 수 있습니다. Memcached는 PHP를 포함한 많은 언어에서 동작하는 인기있는 선택지입니다.

하지만, Memcached 서버를 PHP 스크립트에서 접근하려고 하면, 여러분에게는 아주 바보같은 이름의 두가지 클라이언트 중에 하나를 고를 수 있습니다. MemcacheMemcached가 그것입니다. 둘은 거의 같은 이름을 가진 전혀 다른 라이브러리로 둘다 Memcached 인스턴스에 접근하는데 사용합니다.

Memcached 라이브러리가 Memcached 프로토콜을 가장 잘 구현한 라이브러리로 알려졌습니다. Memcache 라이브러리에는 없는 몇가지 유용한 기능을 포함하고 있고, 가장 활발하게 개발되고 있는 것 같습니다.

하지만, 분산된 서버들에서 Memcached 인스턴스에 접근하는 것이 아니라면, 대신 APCu를 사용하세요. APCu는 PHP 프로젝트에 의해서 지원되고 Memcached와 거의 같은 기능을 제공합니다.

Memached 클라이언트 라이브러리 설치하기

Memcached 서버를 설치하고나면, Memcached 클라이언트 라이브러리를 설치해야합니다. 라이브러리없이는 PHP 스크립트가 Memcached 서버와 통신할 수 없습니다.

Ubuntu 14.04에서 다음 명령을 터미널에 실행하면 Memcached 클라이언트 라이브러리를 설치할 수 있습니다.

sudo apt-get install php5-memcached

APCu를 사용하기

Ubuntu 14.04 이전에는 APC 프로젝트에 opcode 캐시와 Memcached와 비슷한 키-값 저장소 둘 다 있었습니다. 14.04에 포함된 PHP 버전부터는 내장 opcode 캐시를 포함하고, APC는 opcode 캐시 기능을 제외한 ("user cahce"나 APCu의 "u"라고도 하는) APC의 키-값 저장소 기능만 APCu 프로젝트로 분리되었습니다.

APCu 설치하기

Ubuntu 14.04에서 다음 명령을 터미널에 실행하면 APCu를 설치할 수 있습니다.

sudo apt-get install php5-apcu

예제

<?php // Store some values in the APCu cache. We can optionally pass a time-to-live, but in this example the values will live forever until they're garbage-collected by APCu. apcu_store('username-1532', 'Frodo Baggins'); apcu_store('username-958', 'Aragorn'); apcu_store('username-6389', 'Gandalf'); // You can store arrays and objects too. apcu_store('creatures', array('ent', 'dwarf', 'elf')); apcu_store('saruman', new Wizard()); // After storing these values, any PHP script can access them, no matter when it's run! $value = apcu_fetch('username-958', $success); if($success === true) print($value); // Aragorn $value = apcu_fetch('creatures', $success); if($success === true) print_r($value); $value = apcu_fetch('username-1', $success); // $success will be set to boolean false, because this key doesn't exist. if($success !== true) // Note the !==, this checks for true boolean false, not "falsey" values like 0 or empty string. print('Key not found'); apcu_delete('username-958'); // This key will no longer be available. ?>

참고

  • 16.04 이전 APUc 버전에서 APCu 코드를 마이그레이션한다면, 함수 이름이 apc_* 에서 apcu_* 로 바뀐 것에 주의하세요. 예를 들어 apc_store()apcu_store() 로 바뀌었습니다.

더 읽을거리

위로 이동

PHP와 정규표현식

PCRE (preg_*) 함수들을 사용하세요.

PHP는 정규표현식을 사용하는 두가지 방법을 제공합니다. PCRE (Perl 호환, preg_*) 함수와 POSIX (POSIX 확장, ereg_*) 함수입니다.

각 종류의 함수들은 약간 다른 형태의 정규표현식을 사용합니다. 운 좋게도, PHP 5.3.0부터 POSIX 함수들은 deprecated 되었습니다. 그래서, 새로 작성하는 코드는 POSIX 함수로 작성해서는 안됩니다. PCRE 함수(preg_* 함수)를 사용하세요.

더 읽을거리

위로 이동

웹서버에 PHP 올리기

PHP-FPM를 사용하세요.

PHP를 사용하기 위해서 웹서버를 설정하는 여러가지 방법이 있습니다. 전통적(이고 끔찍한) 방법은 Apache의 mod_php를 사용하는 것입니다. Mod_php는 PHP를 Apache 자체에 연결해주지만, Apache는 제대로 관리하지 못합니다. 실제 트래픽을 감당하게 되자마자 심각한 메모리 문제를 격게 될 겁니다.

다음 두 가지 새로운 선택이 빠르게 인기를 얻었습니다. 그것은 mod_fastcgimod_fcgid입니다. 두 가지 모두 제한된 수의 PHP 프로세스를 실행하고, Apache가 요청을 이 인터페이스에 보내서, 이것들이 PHP 실행을 관리하도록 합니다. 이 라이브러리들이 실행할 PHP 프로세스 수를 제한하기 때문에 성능에 영향을 미치지 않으면서 메모리 사용량을 크게 줄일 수 있습니다.

어느 똑똑한 사람들이 특별히 PHP와 아주 잘 동작하도록 설계된 fastcgi의 구현을 만들었는데, 그 이름은 PHP-FPM입니다. 12.04 이후로 이것이 웹서버를 위한 표준 솔루션입니다.

Ubuntu 12.04 출시 몇 년 후에, Apache는 PHP-FPM와 연동할 새로운 방법을 내놓았는데, 바로 mod_proxy_fcgi입니다. 안타깝지만 Ubuntu 14.04에 포함된 Apache 버전에는 이 모듈에 약간 문제가 있습니다. 소켓 기반의 연결을 사용할 수 없고, mod_rewrite관련 이슈와, 404와 같은 페이지에 문제issues with 404 and similar pages가 있습니다. 따라서, Ubuntu 12.04에서 사용했던 검증된 PHP-FPM 사용법을 사용하세요.

아래 예제는 Apache 2.4.7을 위한 것이지만, PHP-FPM는 Nginx와 같은 다른 웹서버들과도 동작합니다.

PHP-FPM과 Apache 설치하기

아래 명령어를 터미널에서 실행하여 Ubuntu 14.04에 PHP-FPM과 Apache를 설치할 수 있습니다.

sudo apt-get install apache2-mpm-event libapache2-mod-fastcgi php5-fpm sudo a2enmod actions alias fastcgi

반드시 apache2-mpm-event (혹은 apache2-mpm-worker)를 사용하고, apache2-mpm-prefork나 apache2-mpm-threaded를 사용해서는 안된다는 것에 주의하세요.

다음으로는, PHP 요청을 PHP-FPM으로 라우팅할 Apache 가상호스트를 설정합니다. 아래 내용을 Apache 설정파일에 넣으세요. (Ubuntu 14.04의 기본 설정 위치는 /etc/apache2/sites-available/000-default.conf).

<Directory /> Require all granted </Directory> <VirtualHost *:80> Action php5-fcgi /php5-fcgi Alias /php5-fcgi /usr/lib/cgi-bin/php5-fcgi FastCgiExternalServer /usr/lib/cgi-bin/php5-fcgi -socket /var/run/php5-fpm.sock -idle-timeout 120 -pass-header Authorization <FilesMatch "\.php$"> SetHandler php5-fcgi </FilesMatch> </VirtualHost>

마지막으로, Apache와 FPM 프로세스를 재시작합니다.

sudo service apache2 restart && sudo service php5-fpm restart

참고

  • SetHandler 디렉티브(directive) 대신에 AddHandler 디렉티브를 사용하는 것은 보안 위험이 있습니다. AddHandler는 ".php"를 파일 이름 중 어디에든 포함한 파일 안의 PHP 코드를 실행할 것 입니다. 그래서 사용자가 파일 업로드 폼(form)을 사용해서 evil.php.gif 파일을 업로드 하면, 예기치못한 불쾌한 일을 당할지도 모릅니다.

더 읽을거리

위로 이동

이메일 보내기

PHPMailer를 사용하세요.

PHPMailer 5.2.7로 테스트 되었음.

PHP는 매우 간단하고 쉬운 mail() 함수를 제공합니다. 유감스럽게도 PHP에 있는 다른 많은 것처럼, 그 단순함에 현혹되어, 있는 그대로 사용했다가는 심각한 보안 문제를 일으킬 수 있습니다.

이메일은 PHP보다도 훨씬 많은 고난의 역사를 가진 프로토콜 세트입니다. PHP의 mail() 함수가 여러분을 오싹하게 만드는 정도로 메일을 발송하는 것은 많은 어려움이 있다고 말하면 적당할 것입니다.

PHPMailer는 메일을 안정적으로 발송하기 위하여 쉬운 인터페이스를 제공하는 유명하고 잘 숙성된(well-aged) 오픈소스 라이브러리입니다. PHPMailer는 모든 어려운 일들을 처리해서 여러분이 좀더 중요한 일에 집중할 수 있도록 해줍니다.

예제

<?php // Include the PHPMailer library require_once('phpmailer-5.2.7/PHPMailerAutoload.php'); // Passing 'true' enables exceptions. This is optional and defaults to false. $mailer = new PHPMailer(true); // Send a mail from Bilbo Baggins to Gandalf the Grey // Set up to, from, and the message body. The body doesn't have to be HTML; check the PHPMailer documentation for details. $mailer->Sender = 'bbaggins@example.com'; $mailer->AddReplyTo('bbaggins@example.com', 'Bilbo Baggins'); $mailer->SetFrom('bbaggins@example.com', 'Bilbo Baggins'); $mailer->AddAddress('gandalf@example.com'); $mailer->Subject = 'The finest weed in the South Farthing'; $mailer->MsgHTML('<p>You really must try it, Gandalf!</p><p>-Bilbo</p>'); // Set up our connection information. $mailer->IsSMTP(); $mailer->SMTPAuth = true; $mailer->SMTPSecure = 'ssl'; $mailer->Port = 465; $mailer->Host = 'my smtp host'; $mailer->Username = 'my smtp username'; $mailer->Password = 'my smtp password'; // All done! $mailer->Send(); ?>

더 읽을거리

위로 이동

이메일 주소 검사하기

filter_var() 함수를 사용하세요.

사용자가 이메일 주소를 올바르게 입력했는지 확인하는 것은 웹 애플리케이션에서 흔하게 해야하는 작업일 것입니다. 여러분은 의심할 나위없이 이 문제를 해결한다고 주장하는 현기증 나는 길이의 복잡한 정규 표현식들을 찾게되겠지만, 가장 쉬운 방법은 PHP의 내장되어 이메일 주소를 검사하는 filter_var() 함수를 사용하는 것입니다.

예제

<?php filter_var('sgamgee@example.com', FILTER_VALIDATE_EMAIL); // Returns "sgamgee@example.com". This is a valid email address. filter_var('sauron@mordor', FILTER_VALIDATE_EMAIL); // Returns boolean false! This is *not* a valid email address. ?>

더 읽을거리

위로 이동

입력과 출력에서 위험한 HTML 값 제거Sanitizing

간단한 위험 요소 제거 작업sanitization에는 htmlentities() 함수를 사용하고, 복잡한 위험 요소 제거 작업에는 HTML Purifier 라이브러리를 사용하세요.

HTML Purifier 4.6.0에서 테스트 됨

모든 웹 애플리케이션에서 사용자 입력값을 표시할 때에 위험할 수 있는 HTML을 제거하기 위해서, 먼저 "깨끗하게 만드는sanitize" 작업은 필수입니다. 만약 웹 애플리케이션에서 직접 출력할 경우에 그것을 보는 사람이 위험할 수 있는 그런 HTML을 악의적인 사용자가 만들어 낼 수 있습니다.

HTML에서 위험 요소를 제거하기 위해서 정규 표현식을 사용하고 싶을 수도 있지만, 그렇게 하지 마세요. HTML은 복잡한 언어이기 때문에 정규 표현식을 사용하여 HTML에서 위험 요소를 제거하려는 모든 시도는 거의 실패하게 됩니다.

strip_tags() 함수를 사용하라는 제안을 찾게될 수도 있습니다. 기술적으로 strip_tags()를 사용하는 것은 안전하지만, 이것은 (닫는 태그가 빠진 것과 같은) 올바르지 않은 HTML이 입력되면 기대하는 것보다 훨씬 많은 내용을 지워버리는 "멍청한" 함수입니다. 기술을 잘 모르는 사용자들non-technical users은 의사 소통시에 종종 <나 > 문자를 사용하기 때문에 strip_tags()는 역시 좋은 선택이 아닙니다.

이메일 주소 검사하기 항목을 읽었다면, filter_var() 함수를 사용하는 것을 고려할지도 모르겠습니다. 하지만, filter_var() 함수는 줄바꿈에 문제가 있고, htmlentities() 함수와 비슷하게 동작하기 위해서 직관적이지 않은 설정이 필요합니다. 따라서, 좋은 선택은 아닙니다.

간단한 위험 요소 제거하기

웹 애플리케이션에서 HTML을 완전히 이스케이프escape(전부 삭제하지는 않고, 위험하지 않은 것만 표시)할 때에는, PHP에 내장된 htmlentities() 함수를 사용하세요. 이 함수는 HTML을 검사하지 않고, 그저 전부 이스케이프하기 때문에 HTML Purifier보다 훨씬 빠릅니다.

htmlentities()는 사촌뻘인 htmlspecialchars()와는 일부가 아닌 모든 적용가능한 HTML 엔티티entities를 변환encodes한다는 점에서 차이가 있습니다.

예제

<?php // Oh no! The user has submitted malicious HTML, and we have to display it in our web app! $evilHtml = '<div onclick="xss();">Mua-ha-ha! Twiddling my evil mustache...</div>'; // Use the ENT_QUOTES flag to make sure both single and double quotes are escaped. // Use the UTF-8 character encoding if you've stored the text as UTF-8 (as you should have). // See the UTF-8 section in this document for more details. $safeHtml = htmlentities($evilHtml, ENT_QUOTES, 'UTF-8'); // $safeHtml is now fully escaped HTML. You can output $safeHtml to your users without fear! ?>

복잡한 위험 요소 제거하기

많은 웹 애플리케이션에서 단순히 HTML을 이스케이프하는 것으로는 부족합니다. 아마도 모든 HTML을 제거하거나 HTML 일부만을 허용하고 싶을겁니다. 그렇게 하기 위해서는 HTML Purifier 라이브러리를 사용하세요.

HTML Purifier는 테스트가 잘 되었지만 느린 라이브러리입니다. 그래서 복잡한 상황이 아닌 경우에는 htmlentities()를 사용해야하는데, 이것이 훨씬 빠르기 때문입니다.

HTML Purifier는 위험 요소를 제거하기 전에 HTML을 검사하기 때문에 strip_tags()보다 낫습니다. 사용자가 잘못된 HTML을 입력하더라도 HTML Purifier는 strip_tags()가 하는 것보다 HTML이 의도한 의미를 보존할 가능성이 더 높습니다. 많은 부분을 커스터마이징할 수 있어서, 출력에서 일부 HTML을 그대로 유지할 수 있는 화이트리스트도 가능합니다.

단점은 꽤 느리다는 것과, 공유 호스팅 환경에서는 적당하지 않을 수도 있는 설정이 필요하고, 문서가 복잡하고 불분명할때가 많다는 것입니다. 다음 예제는 기본 설정입니다. HTML Purifier가 제공하는 더 많은 기능에 대해서는 문서를 읽어보세요.

예제

<?php // Include the HTML Purifier library require_once('htmlpurifier-4.6.0/HTMLPurifier.auto.php'); // Oh no! The user has submitted malicious HTML, and we have to display it in our web app! $evilHtml = '<div onclick="xss();">Mua-ha-ha! Twiddling my evil mustache...</div>'; // Set up the HTML Purifier object with the default configuration. $purifier = new HTMLPurifier(HTMLPurifier_Config::createDefault()); $safeHtml = $purifier->purify($evilHtml); // $safeHtml is now sanitized. You can output $safeHtml to your users without fear! ?>

참고

  • 잘못된 문자 인코딩으로 htmlentities()를 사용하는 것은 예상치 못한 결과를 초래할 수 있습니다. 반드시 이 함수를 사용할 때에는 문자 인코딩을 지정했는지 확인하고, 위험 요소를 제거할 문자열의 인코딩과 일치하는지 확인하세요. 자세한 내용은 UTF-8 항목을 보세요.
  • htmlentities() 사용할 때에는 ENT_QUOTES와 문자 인코딩 인자를 넣어주세요. 기본적으로 htmlentities()는 작은 따옴표를 변환하지 않습니다. 정말 바보같은 기본값이네요!
  • HTML Purifier는 복잡한 HTML에서는 매우 느립니다. 위험 요소를 제거한 결과를 저장했다가 나중에 사용하기 위해서 APC와 같은 캐시 솔루션을 설정하는 것을 고려해보세요.

더 읽을거리

위로 이동

PHP와 UTF-8

한 줄로 끝나는 팁은 없습니다. 조심히 세세하고 일관성있게 작업하세요.

미안하지만, PHP에서 UTF-8은 형편없습니다.

현재 PHP는 내부적으로low level 유니코드를 지원하지 않습니다. UTF-8 문자열을 제대로 처리하도록 강제하는 방법은 있지만, 쉽지 않고, HTML부터 SQL과 PHP까지 웹 애플리케이션의 거의 모든 부분을 파해쳐야 합니다. 간략하게 실용적인 내용을 정리해보겠습니다.

PHP에서의 UTF-8

두 문자열을 연결해서 변수에 문자열을 할당하는 것과 같은 기본 문자열 연산은 UTF-8을 위해서 아무 것도 필요없습니다. 하지만, strpos()strlen()과 같은 대부분의 문자열 함수는 특별히 주의해야 합니다. 이런 함수들은 보통 mb_*로 시작하는 대응 함수가 있습니다. 예를 들자면, mb_strpos()mb_strlen()이 있습니다. 이렇게 대응하는 함수들을 묶어서 멀티바이트 문자열 함수Multibyte String Functions라고 부릅니다. 멀티 바이트 문자열 함수들은 유니코드 문자열에 동작하도록 만들어졌습니다.

유니코드 문자열을 다룰때에는 반드시 mb_* 함수를 사용하세요. UTF-8 문자열에 substr()을 사용하면, 알아볼 수 없는 반쪽짜리 문자를 포함한 결과를 얻게될 가능성이 높습니다. 이럴때 사용해야할 함수는 대응하는 멀티바이트 문자열 함수인 mb_substr()입니다.

어려운 점은, 항상 mb_* 함수를 사용해야 한다는 것을 기억해야한다는 것입니다. 단 한번이라도 잊게되면, 여러분의 유니코드 문자열은 처리과정에서 알아볼 수 없게 되어버릴 것입니다.

모든 문자열 함수에 mb_* 대응 함수가 있는 것은 아닙니다. 여러분이 하려는 것에 대응 함수가 없다면, 운이 없다고 생각하세요.

그리고, 작성하는 모든 PHP 스크립트 상단에 (또는, 전역으로 포함하는 스크립트의 상단에) mb_internal_encoding() 함수를 사용하세요. 스크립트가 브라우저에 출력한다면, 바로 뒤에 mb_http_output() 함수를 사용하세요. 모든 스크립트에 문자열의 인코딩을 명시적으로 정의하는 것이 앞으로 있을 수 있는 많은 두통거리를 막아줍니다.

마지막으로, 문자열을 다루는 많은 PHP 함수들은 문자 인코딩을 지정할 수 있는 부가optional 매개변수가 있습니다. 가능할 경우, 항상 명시적으로 UTF-8을 지정하세요. 예를 들어 htmlentities()는 문자 인코딩을 위한 옵션이 있는데, UTF-8 문자열을 다룰때에는 항상 UTF-8을 명시적으로 지정하세요.

OS에서의 UTF-8

종종 파일명이나 그 내용이 유니코드로 인코딩된 파일들을 발견하게 될 것입니다. PHP는 리눅스나 윈도우즈를 포함하여 여러가지 운영체제에서 동작하지만, 슬프게도 각 운영체제의 특성에 의해서 각 플랫폼별로 다르게 유니코드 파일명을 취급합니다.

리눅스나 OSX는 UTF-8 파일명을 적절히 잘 다루고 있는 것으로 보이지만, 윈도우즈는 그렇지 못합니다. 만약 윈도우즈에서 PHP를 사용해서 아스키 문자가 아닌 문자의 파일명으로 파일을 저장한다면, 아마도 이상하게 깨진 문자로 파일명이 표시되는 것을 보게될 것입니다.

간단한 방법은 아니지만, 이식성을 갖는 방법은 다음과 같습니다. 리눅스나 OSX에서는 파일이름을 UTF-8로 인코딩할 수 있지만, 윈도우즈에서는 ISO-8859-1로 인코딩해야합니다.

만약에 윈도우즈에서 동작하고 있는지 확인하는 번거로움을 피하고 싶다면, 파일을 저장하기 전에 파일명을 항상 URL encode 할 수도 있습니다. 이것은 아스키코드로 유니코드 문자를 표기하여, 플랫폼에 따른 유니코드 특성을 우회하는 효과적인 방법입니다.

MySQL에서의 UTF-8

PHP 스크립트가 MySQL에 접속한다면, 위에서 다룬 모든 예방 조치를 하더라도 데이터베이스에 비 UTF-8 문자열로 저장될 가능성이 있습니다.

PHP에서 MySQL로 문자열을 확실하게 UTF-8로 전달하기 위해서, 데이터베이스와 테이블 모두 utf8mb4 문자셋character set과 collation으로 설정하고, PDO 연결 문자열PDO connection string에서도 utf8mb4 문자셋을 사용하세요. 예제는 MySQL 데이터베이스에 연결하고 쿼리하기 항목을 참고하세요. 이것은 정말 중요합니다.

완전한 UTF-8 지원을 위해서 utf8이 아니라 utf8mb4 문자셋을 사용해야하는 것에 주의하세요! 그 이유는 더 읽을거리를 참고하세요.

브라우저에서의 UTF-8

PHP 스크립트가 UTF-8 문자열을 브라우저에 출력하도록 mb_http_output() 함수를 사용하세요. HTML 페이지의 <head> 태그에 charset 메타 태그도 넣으세요.

예제

<?php // Tell PHP that we're using UTF-8 strings until the end of the script mb_internal_encoding('UTF-8'); // Tell PHP that we'll be outputting UTF-8 to the browser mb_http_output('UTF-8'); // Our UTF-8 test string $string = 'Êl síla erin lû e-govaned vîn.'; // Transform the string in some way with a multibyte function // Note how we cut the string at a non-Ascii character for demonstration purposes $string = mb_substr($string, 0, 15); // Connect to a database to store the transformed string // See the PDO example in this document for more information // Note that we define the character set as utf8mb4 in the PDO connection string $link = new \PDO( 'mysql:host=your-hostname;dbname=your-db;charset=utf8mb4', 'your-username', 'your-password', array( \PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION, \PDO::ATTR_PERSISTENT => false ) ); // Store our transformed string as UTF-8 in our database // Your DB and tables are in the utf8mb4 character set and collation, right? $handle = $link->prepare('insert into ElvishSentences (Id, Body) values (?, ?)'); $handle->bindValue(1, 1, PDO::PARAM_INT); $handle->bindValue(2, $string); $handle->execute(); // Retrieve the string we just stored to prove it was stored correctly $handle = $link->prepare('select * from ElvishSentences where Id = ?'); $handle->bindValue(1, 1, PDO::PARAM_INT); $handle->execute(); // Store the result into an object that we'll output later in our HTML $result = $handle->fetchAll(\PDO::FETCH_OBJ); ?><!doctype html> <html> <head> <meta charset="UTF-8" /> <title>UTF-8 test page</title> </head> <body> <?php foreach($result as $row){ print($row->Body); // This should correctly output our transformed UTF-8 string to the browser } ?> </body> </html>

더 읽을거리

위로 이동

날짜와 시간 다루기

DateTime 클래스를 사용하세요.

PHP의 옛 암흑기에는 date(), gmdate(), date_timezone_set(), strtotime() 등 갈피를 잡기 힘든 조합으로 날짜와 시간을 다룰 수 밖에 없었습니다. 슬프게도 여전히 많은 온라인 학습서들에 이런 어려운 옛 방식의 함수들을 소개하는 것을 볼 수 있습니다.

다행히도 우리가 이야기하고 있는 버전의 PHP에는 매우 친절한 DateTime 클래스가 있습니다. 이 클래스는 옛 방식의 함수들의 모든 기능에 더해서 더 많은 기능을 하나의 사용하기 쉬운 클래스에 담고 있습니다. 덤으로 훨씬 간편하게 시간대 변환도 할 수 있습니다. PHP에서 날짜를 만들고, 비교하거나, 변경하고, 표시할 때에는 항상 DateTime 클래스를 사용하세요.

예제

<?php // Construct a new UTC date. Always specify UTC unless you really know what you're doing! $date = new DateTime('2011-05-04 05:00:00', new DateTimeZone('UTC')); // Add ten days to our initial date $date->add(new DateInterval('P10D')); echo($date->format('Y-m-d h:i:s')); // 2011-05-14 05:00:00 // Sadly we don't have a Middle Earth timezone // Convert our UTC date to the PST (or PDT, depending) time zone $date->setTimezone(new DateTimeZone('America/Los_Angeles')); // Note that if you run this line yourself, it might differ by an hour depending on daylight savings echo($date->format('Y-m-d h:i:s')); // 2011-05-13 10:00:00 $later = new DateTime('2012-05-20', new DateTimeZone('UTC')); // Compare two dates if($date < $later) echo('Yup, you can compare dates using these easy operators!'); // Find the difference between two dates $difference = $date->diff($later); echo('The 2nd date is ' . $difference->days . ' later than 1st date.'); ?>

참고

  • 시간대를 지정하지 않으면, DateTime::__construct()동작 중인 컴퓨터의 시간대로 시간대를 설정하여 날짜를 만듭니다. 이것은 나중에 굉장히 골치 아픈 일을 초래할 수 있습니다. 잘 모르겠으면 새로운 날짜를 만들때 항상 UTC 시간대로 설정하세요.

  • DateTime::__construct()에 유닉스 타임 스탬프timestamp를 사용하면, 두번째 매개변수에 어떤 값이 있든지 시간대는 항상 UTC 로 설정됩니다.

  • DateTime::__construct()에 0으로 된 날짜(예. “0000-00-00”, 보통 MySQL이 DateTime 컬럼의 기본 값으로 생성하는 값)를 넘기면, “0000-00-00”이 아니라 엉뚱한 날짜가 됩니다.

  • 32비트 시스템에서 DateTime::getTimestamp()는 2038년 이후의 시간은 표시하지 못합니다. 64비트 시스템은 문제 없습니다.

더 읽을거리

위로 이동

값이 null인지 false인지 확인하기

null과 false 값을 확인할 때에는 === 연산자를 사용하세요.

PHP의 느슨한 형식의 시스템loose typing system은 변수의 값을 확인하는 많은 방법을 제공합니다. 하지만 이는 많은 문제를 안고 있기도 합니다. == 연산자를 사용해서 값이 null인지 false인지 확인하는 것은 값이 빈 문자열이나 0일 경우에 참으로 잘못 판단false positives할 수 있습니다. isset()은 변수가 NULL이 아닌 값을 가지고 있는지 확인하지만, 부울값 false인지는 확인할 수 없습니다.

is_null() 함수는 값이 null인지 확인하고, is_bool() 함수는 값이 (false와 같은) 부울 값인지 확인하지만, 더 나은 것이 있습니다. 바로 === 연산자입니다. === 연산자는 느슨한 형식의 PHP 환경에서 동등한equivalent 것이 아니라 값이 동일한지identical 확인합니다. 또한, is_null()이나 is_bool()보다 약간 빠르고, 비교를 위해서 함수를 사용하는 것보다 보기 좋습니다.

예제

<?php $x = 0; $y = null; // Is $x null? if($x == null) print('Oops! $x is 0, not null!'); // Is $y null? if(is_null($y)) print('Great, but could be faster.'); if($y === null) print('Perfect!'); // Does the string abc contain the character a? if(strpos('abc', 'a')) // GOTCHA! strpos returns 0, indicating it wishes to return the position of the first character. // But PHP interpretes 0 as false, so we never reach this print statement! print('Found it!'); //Solution: use !== (the opposite of ===) to see if strpos() returns 0, or boolean false. if(strpos('abc', 'a') !== false) print('Found it for real this time!'); ?>

참고

  • strpos()와 같이 0이나 false값을 리턴하는 함수의 리턴값을 확인할때에는, 항상 === 연산자와 !== 연산자를 사용하세요. 그렇지 않으면 문제가 발생합니다.

더 읽을거리

위로 이동

제안이나 수정사항

읽어주셔서 감사합니다! 여러분이 미리 파악하고 있지 않는다면, PHP는 복잡하고 위험들로 가득 차있습니다. 저도 사람이기에 이 문서에는 어떤 실수가 있을 수 있습니다.

이 문서에 어떤 제안이나 수정할 것이 있으면, 최신 개정판 & 관리자 항목의 정보를 참고하셔서 연락주세요.

한국어판 번역에 대한 제안이나 수정 사항은 Github 저장소에 이슈를 남겨주세요.

위로 이동