PHP의 전역 변수가 나쁜 습관으로 간주됩니까? 그렇다면 그 이유는 무엇입니까?


86
function foo () {
    global $var;
    // rest of code
}

내 작은 PHP 프로젝트에서는 보통 절차적인 방식으로 진행합니다. 일반적으로 시스템 구성을 포함하는 변수가 있으며 함수에서이 변수에 액세스해야 할 때 global $var;.

이것은 나쁜 습관입니까?


19
전역 변수 나쁜 연습 동의어입니다
L̳o̳̳n̳̳g̳̳p̳o̳̳k̳̳e̳̳


2
단위 / 수락 테스트를 시도하면 전역이 문제인 이유를 빠르게 찾을 수 있습니다. 두 번 이상 작업을 수행하면 코드를 신뢰할 수 없게 만듭니다.
Kzqai

답변:


102

사람들이 다른 언어로 전역 변수에 대해 이야기 할 때 그것은 PHP에서하는 것과는 다른 것을 의미합니다. 변수는 PHP에서 실제로 전역 적이 지 않기 때문 입니다. 일반적인 PHP 프로그램의 범위는 하나의 HTTP 요청입니다. 세션 변수는 일반적으로 많은 HTTP 요청을 포함하기 때문에 실제로 PHP "전역"변수보다 더 넓은 범위를 갖습니다.

종종 (항상?) 다음과 preg_replace_callback()같은 메서드에서 멤버 함수를 호출 할 수 있습니다 .

preg_replace_callback('!pattern!', array($obj, 'method'), $str);

자세한 내용은 콜백 을 참조하십시오 .

요점은 객체가 PHP에 고정되어 어떤면에서 어색함을 유발한다는 것입니다.

다른 언어의 표준이나 구조를 PHP에 적용하는 것에 지나치게 신경 쓰지 마십시오. 또 다른 일반적인 함정은 모든 것 위에 객체 모델을 붙임으로써 PHP를 순수한 OOP 언어로 바꾸는 것입니다.

다른 무엇과 마찬가지로 "글로벌"변수, 절차 적 코드, 특정 프레임 워크 및 OOP를 사용하십시오. 이는 의미가 있고, 문제를 해결하고, 작성해야하는 코드의 양을 줄이거 나, 생각 때문이 아니라 유지 관리가 쉽고 이해하기 쉽기 때문입니다. 당신은해야합니다.


8
PHP 5.3은 람다 함수를 사용하여이 중 일부를 해결하므로 콜백에 대해 전역 범위에서 선언 된 함수를 사용하지 않도록 할 수 있습니다. 유지 가능하고 읽기 쉬운 코드 조언을위한 +1
Jonathan Fingland

폼의 콜백 사용할 수 없습니다 array ($obj, 'callbackMethod')에 호출에를 preg_replace_callback()? (I ... 난이 OOP의 함정에 떨어진 먹이 것, 알고)
grossvogel

25
질문은 "전역 변수를 사용해야 하는가?"가 아니 었습니다. 이에 대한 대답은 '필요한 경우 가끔 확인'이 될 것입니다. 문제는 그들이 나쁜 습관입니다. 대답은 '예, 가끔'입니다. 포스터 작은 프로젝트의 경우 나쁜 것은 없습니다. 그러나 많은 팀원과 움직이는 부분이 많은 대규모 프로젝트의 경우 전역 변수를 많이 사용하면 코드를 디버그하기 어렵고 리팩토링하기가 거의 불가능하며 읽다. 가끔 사용할 수 있나요? .. 물론입니다.
eddiemoya

@eddiemoya 잘 Eddie가 말했다. 전역 변수를 사용하는 것과 같은 나쁜 관행을 정당화하는 사람들이 너무 많습니다. 전염병처럼 그들을 피해야합니다. 괜찮은 소프트웨어 공학 학위라면 이걸 당신에게 드릴 것입니다. 강사들은 단지 지옥에 대해 말하는 것이 아닙니다 ... 그들은 수십 년의 경험을 통해 알고 있습니다. 필요한 값에 액세스하려면 가능한 경우 멤버 함수를 사용해야합니다 (예 : Wordpress의 get_query_var () 등).

27

주의 깊게 사용하지 않으면 전역 변수를 사용하면 문제를 찾기가 더 어려워 질 수 있습니다. PHP 스크립트를 요청하고 일부 함수에 존재하지 않는 배열의 인덱스에 액세스하려고한다는 경고가 표시되었다고 가정 해 보겠습니다.

액세스하려는 배열이 함수에 로컬 인 경우 함수에서 실수를했는지 확인합니다. 함수 입력에 문제가있을 수 있으므로 함수가 호출 된 위치를 확인합니다.

그러나 해당 배열이 전역 인 경우 전역 변수를 사용하는 모든 위치를 확인해야하며 그뿐만 아니라 전역 변수에 대한 참조가 액세스되는 순서를 파악해야합니다.

코드 조각에 전역 변수가있는 경우 해당 코드의 기능을 분리하기가 어렵습니다. 기능을 분리하려는 이유는 무엇입니까? 따라서 테스트하고 다른 곳에서 재사용 할 수 있습니다. 코드가있는 경우 테스트 할 필요가없고 재사용 할 필요가없는 경우 전역 변수를 사용하는 것이 좋습니다.


하지만 오류는 주로 스크립트가 어떤 파일 / 줄에서 그렇게 깨지는지를 보여줍니다 .. 여기서 문제가 보이지 않습니다
samayo dec

8
대본이 끊어진 곳! = 실수 한 곳.
HonoredMule 2015-06-26

16

나는 클레 투스에 동의합니다. 두 가지를 추가합니다.

  1. 접두사를 사용하여 즉시 글로벌로 식별 할 수 있습니다 (예 : $ g_).
  2. 한 자리에 선언하고 코드 전체에 뿌리지 마십시오.

안부, 돈


1
Jeah, 저는 항상 전역 적으로 사용하려는 변수 앞에 밑줄을 붙입니다.
KRTac

9
@KRTac이지만 $ _testVariable은 일반적으로 개인 변수로 이해됩니다. 이는 전역 변수가 아닌 개인 변수를 정의하는 비공식 표준입니다.
Aditya MP

6
일반적인 관행은 ALL CAPS를 사용하여 전역 변수를 정의하는 것입니다. 예 :$DB = 'foo';
pixeline

7

누가 경험, 대학 학위 및 소프트웨어 공학에 대해 반대 할 수 있습니까? 나 말고. 객체 지향 단일 페이지 PHP 응용 프로그램을 개발할 때 네임 스페이스 충돌에 대해 걱정하지 않고 처음부터 전체를 빌드 할 수 있다는 사실을 알 때 더 재미 있다고 말할뿐입니다. 처음부터 건축하는 것은 많은 사람들이 더 이상하지 않는 일입니다. 그들은 직업, 기한, 보너스 또는 관심을 가질 평판이 있습니다. 이러한 유형은 위험이 큰 사전 빌드 된 코드를 너무 많이 사용하는 경향이 있으므로 전역 변수를 전혀 사용할 위험이 없습니다.

프로그램의 전역 영역에서만 사용 되더라도 전역 변수를 사용하는 것은 나쁠 수 있습니다.하지만 재미 있고 무언가를 만들고 싶은 사람들을 잊지 마십시오 .

이것이 전역 네임 스페이스에서 몇 개의 변수 (<10)를 사용하는 것을 의미한다면, 프로그램의 전역 영역에서만 사용됩니다. 그래, 그래, MVC, 의존성 주입, 외부 코드, 어쩌구, 어쩌구, 어쩌구. 그러나 코드의 99.99 %를 네임 스페이스와 클래스에 포함하고 외부 코드가 샌드 박스 처리 된 경우 전역 변수를 사용하면 세계가 끝나지 않습니다 (반복합니다. 세계가 끝나지 않습니다).

일반적으로 전역 변수를 사용하는 것이 좋지 않다고 말하지 않습니다 . 프로그램의 전역 영역 밖에서 전역 변수 (플래그 등)를 사용하는 것은 문제를 요구 하고 (장기적으로) 상태를 쉽게 추적 하지 못할 수 있기 때문에 부적절 하다고 말할 수 있습니다. 또한 더 많이 배울수록 사용과 관련된 버그를 추적하는 "즐거움"을 경험하게되므로 전역 변수에 대한 의존도가 낮아질 것입니다 . 이것만으로도 같은 문제를 해결할 다른 방법을 찾도록 장려 할 것입니다. 우연히도 이것은 PHP 사용자를 네임 스페이스와 클래스 (정적 멤버 등) 사용 방법을 배우는 방향으로 밀어 붙이는 경향이 있습니다.

컴퓨터 과학 분야는 방대합니다. 우리가 그것을 나쁘게 표시 하기 때문에 모든 사람이 무언가를하지 못하도록 겁 을 주면, 그들은 레이블 뒤에있는 이유를 진정으로 이해하는 재미를 잃게됩니다.

필요한 경우 전역 변수를 사용하고 이러한 변수 없이도 문제를 해결할 수 있는지 확인하십시오. 충돌, 테스트 및 디버깅은 문제에 대한 설명뿐만 아니라 문제의 실제 특성을 자세히 이해할 때 더 많은 것을 의미합니다.


3

종료 된 SO 문서 베타에서 재 게시 됨

이 문제를 다음 의사 코드로 설명 할 수 있습니다.

function foo() {
     global $bob;
     $bob->doSomething();
}

첫 번째 질문은 분명한 것입니다.

어디 $bob에서 왔습니까?

헷갈 리나요? 좋은. 방금 글로벌이 혼란스럽고 나쁜 습관으로 간주되는 이유를 배웠습니다. 이것이 실제 프로그램이라면, 다음 번 재미는 모든 인스턴스를 추적 $bob하고 올바른 프로그램을 찾기를 바라는 것 $bob입니다. 더 나쁜 것은 다른 사람이 가서 정의하면 $bob(또는 해당 변수를 잊고 재사용 한 경우) 코드가 손상 될 수 있습니다 (위 코드 예제에서 잘못된 객체가 있거나 객체가 전혀 없으면 치명적인 오류가 발생할 수 있습니다). 거의 모든 PHP 프로그램은 코드를 include('file.php');유지하는 작업 과 같은 코드를 사용 하므로 파일을 더 많이 추가할수록 기하 급수적으로 어려워집니다.

글로벌을 피하려면 어떻게해야합니까?

전역을 피하는 가장 좋은 방법은 Dependency Injection 이라는 철학 입니다. 여기서 필요한 도구를 함수 나 클래스에 전달합니다.

function foo(\Bar $bob) {
    $bob->doSomething();
}

이것은 이해하고 유지하기 가 훨씬 쉽습니다. $bob호출자가 그것을 아는 책임이 있기 때문에 어디에 설정 되었는지 추측 할 수 없습니다 (알아야 할 것을 전달합니다). 더 좋은 것은 전달되는 것을 제한하기 위해 타입 선언 을 사용할 수 있다는 것 입니다. 우리가 알 수 있도록 그 $bob의 인스턴스 중 하나입니다 Bar클래스, 또는 아이의 인스턴스 Bar우리는 우리가 그 클래스의 방법을 사용할 수 있습니다 알고 의미합니다. 표준 자동 로더 (PHP 5.3부터 사용 가능)와 결합하여 이제 Bar정의 된 위치를 추적 할 수 있습니다 . PHP 7.0 이상에는 확장 된 유형 선언이 포함되어 있으며, 여기서 int또는 같은 스칼라 유형을 사용할 수도 있습니다 string.


$ bob을 모든 곳에 전달해야하는 대안은 Bar 클래스를 싱글 톤으로 만들고 Bar 자체 내에 Bar 인스턴스를 정적으로 저장하고 정적 메서드를 사용하여 객체를 인스턴스화 / 페치하는 것입니다. 그런 다음 $bob = Bar::instance();필요할 때마다 할 수 있습니다 .
Scoots

1
Singleton은 안티 패턴으로 간주됩니다 . 의존성 주입은 이러한 함정을 피합니다
Machavity

2
링크 한 해당 게시물에 어느 정도의 논쟁이 있습니다 (예를 들어, 수락 된 답변에 대한 최고 평점 댓글과 두 번째로 찬성 응답이 모두 수락 된 답변과 강력하게 동의하지 않음), 싱글 톤 사용이 즉석에서 해고되지 않고 사례별로 고려됩니다.
Scoots

0

같이:

global $my_global; 
$my_global = 'Transport me between functions';
Equals $GLOBALS['my_global']

나쁜 습관입니다 (Wordpress처럼 $pagenow) ... 흠

간결하게 :

$my-global = 'Transport me between functions';

PHP 오류 이지만 :

$GLOBALS['my-global'] = 'Transport me between functions';

오류 가 아닙니다 . hypen은 .NET 과 같은 "일반적인"사용자 선언 변수와 충돌하지 않습니다$pagenow . 대문자 사용은 사용중인 슈퍼 글로벌을 나타내며 코드에서 쉽게 발견 하거나 파일에서 찾기로 추적 합니다.

단일 솔루션에 대한 모든 클래스를 빌드하기 위해 게으른 경우 하이픈을 사용합니다.

$GLOBALS['PREFIX-MY-GLOBAL'] = 'Transport me ... ';

그러나 더 광범위한 사용의 경우 ONE 전역을 배열로 사용합니다.

$GLOBALS['PREFIX-MY-GLOBAL']['context-something'] = 'Transport me ... ';
$GLOBALS['PREFIX-MY-GLOBAL']['context-something-else']['numbers'][] = 'Transport me ... ';

후자는 나를 위해 "콜라 라이트"목표 또는 사용에 대한 좋은 연습 이며 매번 일부 데이터를 "캐시"하기 위해 단일 클래스로 어수선하게하는 대신입니다. 내가 틀렸거나 여기에서 어리석은 것을 놓친 경우 의견을 말하십시오 ...

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.