PHP 중첩 함수는 무엇입니까?


80

자바 스크립트에서 중첩 된 함수는 매우 유용합니다 : 클로저, 개인 메서드 및 무엇을 가지고 있는지 ..

중첩 된 PHP 함수는 무엇입니까? 누구든지 그들을 사용하고 무엇을 위해?

내가 한 작은 조사가 있습니다.

<?php
function outer( $msg ) {
    function inner( $msg ) {
        echo 'inner: '.$msg.' ';
    }
    echo 'outer: '.$msg.' ';
    inner( $msg );
}

inner( 'test1' );  // Fatal error:  Call to undefined function inner()
outer( 'test2' );  // outer: test2 inner: test2
inner( 'test3' );  // inner: test3
outer( 'test4' );  // Fatal error:  Cannot redeclare inner()

1
나는 이것에 대한 지원이 PHP6에서 삭제되었다는 것을 읽었다 고 맹세 할 수 있었지만 어디에서도 찾을 수 없습니다.
Greg

2
@greg 어쨌든 PHP6에 대한 전체 계획이 공중에 떠 있다고 생각 했습니까?
James

그들은 큰 기능있는 거 좋은 - 그렇다고 재귀 적 조직
JVE999

PHP에서도 클로저가 있습니다.
Gralgrathor

답변:


88

기본적으로 없습니다. 나는 항상 이것을 파서의 부작용으로 취급했습니다.

Eran Galperin은 이러한 기능이 왠지 사적인 것이라고 잘못 생각합니다. 그들은 단순히 선언 될 때까지outer()실행될 . 또한 비공개 범위가 아닙니다. 비록 지연 되긴했지만 글로벌 범위를 오염시킵니다. 그리고 콜백으로서 외부 콜백은 여전히 ​​한 번만 호출 될 수 있습니다. 나는 여전히 별칭을 두 번 이상 호출하는 배열에 적용하는 것이 얼마나 도움이되는지 알지 못합니다.

내가 파헤칠 수있는 유일한 '실제 세계'의 예는 this 이며, 한 번만 실행할 수 있고 더 깔끔하게 다시 작성할 수있는 IMO입니다.

내가 생각할 수있는 유일한 용도는 모듈 [name]_include이 전역 공간에 여러 중첩 된 메서드를 설정 하는 메서드 를 호출하는 것입니다.

if (!function_exists ('somefunc')) {
  function somefunc() { }
}

체크 무늬.

PHP의 OOP가 분명히 더 나은 선택이 될 것입니다. :)


9
그래 진짜. 잔인하게 나쁘다.
Tony Arkles

1
훌륭한 예제 링크. 상속 대신 구현을 시작해야합니다!
zanlok

같은 def루비의 선언
user102008

1
비록 그것들이 정확히 사적인 함수는 아니지만, 외부 함수가 호출되지 않는 한 여전히 호출 될 수 없습니다. 그래서 이것은 그들에게 외부 함수와 "함께"실행될 함수로서 일종의 의존성을 제공합니다 ...
techexpert

2
이 동작이 없으면 자동로드가 작동하지 않습니다. 함수 내부의 선언이 어떻게 든 비공개 인 경우 자동로드 핸들러가 수행하는 include / require는 아무 작업도하지 않습니다.
cleong

88

PHP 5.3을 사용하는 경우 익명 함수로 Javacript와 유사한 동작을 더 많이 얻을 수 있습니다.

<?php
function outer() {
    $inner=function() {
        echo "test\n";
    };

    $inner();
}

outer();
outer();

inner(); //PHP Fatal error:  Call to undefined function inner()
$inner(); //PHP Fatal error:  Function name must be a string
?>

산출:

test
test

11
+1 실제로 기능적인 대답으로 (기본적으로) 기능 항목을 응답, 그리고 OOP에 대한
피터 호스트

질문 작성자는 수락 된 질문을이 질문으로 업데이트해야합니다. 이것은 실제로 내 시간까지 질문에 대한 대답입니다.

9

[@PierredeLESPINAY의 댓글에 따라 다시 작성되었습니다.]

부작용 일뿐 아니라 실제로 동적으로 수정하는 데 매우 유용한 기능입니다. 프로그램의 논리 하는 . 절차 적 PHP 시대에서 왔지만, 가능한 가장 간단한 방법으로 특정 독립형 함수에 대한 대체 구현을 제공하려는 경우 OO 아키텍처에서도 유용 할 수 있습니다. (OO가 대부분의 경우 더 나은 선택이지만 의무가 아니라 옵션이며 일부 간단한 작업에는 추가 작업이 필요하지 않습니다.)

예를 들어, 프레임 워크에서 플러그인을 동적 / 조건부로로드하고 플러그인 작성자의 수명을 매우 쉽게 만들고 싶다면 플러그인이 재정의하지 않은 일부 중요한 기능에 대한 기본 구현을 제공 할 수 있습니다.

<?php // Some framework module

function provide_defaults()
{
    // Make sure a critical function exists:
    if (!function_exists("tedious_plugin_callback"))
    {
        function tedious_plugin_callback()
        {
        // Complex code no plugin author ever bothers to customize... ;)
        }
    }
}

2
그러나, OP에 따르면, 중첩 된 함수의 범위는 ... 컨테이너 기능에 국한되지 것 같다
피에르 드 LESPINAY

1
@PierredeLESPINAY : 죄송합니다, 매우 사실입니다. 지적 해 주셔서 감사합니다! : -o 그에 따라 답변을 업데이트 (재 작성)했습니다. (즉, 여전히 매우 편리한 기능이지만 완전히 다른 이유가 있습니다.)
Sz.

7

함수 내에 정의 된 함수는 많이 사용할 수 없지만 조건부로 정의 된 함수는 할 수 있습니다. 예를 들면 :

if ($language == 'en') {
  function cmp($a, $b) { /* sort by English word order */ }
} else if ($language == 'de') {
  function cmp($a, $b) { /* sort by German word order; yes it's different */ }
} // etc

그런 다음 코드에서해야 할 일은 usort () 호출과 같은 일에서 'cmp'함수를 사용하는 것이므로 코드 전체에 걸쳐 언어 검사를 낭비하지 않아도됩니다. 이제 나는 이것을하지 않았지만 그것을하기위한 논쟁을 볼 수 있습니다.


1
과거에는이 자체 수정 코드를 호출했을 것입니다. 훌륭한 도구이지만 남용에 대한 GOTO만큼 위험합니다 ...
Killroy

2
나쁜 생각. 더 나은 : OO를 사용하고 스크립팅 엔진 세부 사항을 해킹하지 마십시오.
zanlok

1
익명 함수가 할당 된 변수를 설정 해제 할 수 있습니다.
BF

4

위에서 말한 모든 것, 함수 내에서 지역화되고 반복적 인 코드 (부모 함수 내에서만 사용됨)를 대체하기 위해 중첩 함수를 만들 수 있습니다. 익명 함수가 이에 대한 완벽한 예입니다.

일부는 클래스에서 전용 메서드 (또는 더 작은 코드 블록)를 생성한다고 말할 수 있지만, 이는 초 특이 작업 (부모에게만 해당)을 모듈화해야 할 때 물을 혼란스럽게하지만 나머지 사람들이 반드시 사용할 수있는 것은 아닙니다. 수업. 좋은 소식은 다른 곳에서 해당 기능이 필요하다는 것이 밝혀지면 수정이 다소 기본적이라는 것입니다 (정의를 더 중심 위치로 이동).

일반적으로 JavaScript를 표준으로 사용하여 다른 C 기반 프로그래밍 언어를 평가하는 것은 좋지 않습니다. JavaScript는 PHP, Python, Perl, C, C ++ 및 Java와 비교할 때 확실히 그 자체의 동물입니다. 물론 일반적인 유사점은 많지만 핵심적인 세부 사항 (참조 JavaScript : The Definitive Guide, 6th Edition, Chapters 1-12 )에주의를 기울이면 핵심 JavaScript를 독특하고 아름답고 다르며 단순하게 만들고 동시에 복잡합니다. 그것은 내 2 센트입니다.

명확하게 말하면 중첩 함수가 비공개라고 말하는 것이 아닙니다. 그 중첩만으로도 사소한 것을 모듈화해야 할 때 (부모 함수에서만 필요함) 혼란을 피할 수 있습니다.


2

내 모든 PHP는 OO이지만, 특히 함수가 재귀적이고 반드시 객체가 아닐 때 중첩 함수가 사용되는 것으로 보입니다. 즉, 중첩 된 함수 외부에서는 호출되지 않지만 재귀 적이므로 나중에 함수 여야합니다.

하나의 다른 방법을 명시 적으로 사용하기위한 새로운 방법을 만드는 데는 별 의미가 없습니다. 나에게 그것은 서투른 코드이며 OO의 요점은 아닙니다. 다른 곳에서는이 함수를 호출하지 않으려면 중첩하십시오.


1
당신은 돈을 많이 벌고 있지만 더 나은 예는 array_filter (), array_map (), preg_replace_callback (), uasort () 등에 대한 콜백 함수를 선언 할 때입니다. 이 함수를 상당한 빈도로 사용하고 호출하는 OOP 메서드 외부에서 선언하는 콜백이 거의 필요하지 않으므로 콜백 함수로 전역 또는 클래스 네임 스페이스를 오염시키는 것을 피하는 것이 훨씬 깔끔해 보입니다. . 그리고 마침내 PHP 5.3으로 할 수 있습니다 (user614643의 답변에서 설명했듯이)!
Derek

1

웹 서비스 호출에서 우리는 중첩 된 방식으로 1,000 개의 함수로 가득 찬 라이브러리에 대한 개별 함수를 동적으로 포함하여 훨씬 낮은 오버 헤드 (메모리 및 속도)를 발견했습니다. 일반적인 호출 스택은 5-10 호출 깊이 일 수 있습니다. 12 개의 1-2kb 파일을 동적으로 연결하는 것이 메가 바이트를 포함하는 것보다 낫습니다. 이것은 래핑이 필요한 작은 util 함수를 생성함으로써 수행되었습니다. 포함 된 함수는 호출 스택 위의 함수 내에 중첩됩니다. 모든 웹 서비스 호출에 필요하지는 않았지만 php의 내장 된 지연 로딩 기능을 사용할 수 있었던 수백 개의 함수로 가득 찬 클래스와는 대조적으로 생각해보십시오.


1

만약 당신이 php 7에 있다면 다음을보십시오 : 이 구현은 중첩 된 함수에 대한 명확한 아이디어를 제공 할 것입니다. 함수 foo ()에 중첩 된 세 가지 함수 (too (), boo () 및 zoo ())가 있다고 가정합니다. boo () 및 zoo ()에는 동일한 이름의 중첩 함수 xoo ()가 있습니다. 이제이 코드에서 중첩 함수의 규칙을 명확하게 설명했습니다.

   function foo(){
        echo 'foo() is called'.'<br>';
        function too(){
            echo 'foo()->too() is called'.'<br>';
        }
        function boo(){
            echo 'foo()->boo() is called'.'<br>';
            function xoo(){
                echo 'foo()->boo()->xoo() is called'.'<br>';
            }
            function moo(){
                echo 'foo()->boo()->moo() is called'.'<br>';
            }
        }
        function zoo(){
            echo 'foo()->zoo() is called'.'<br>';
            function xoo(){     //same name as used in boo()->xoo();
                echo 'zoo()->xoo() is called'.'<br>';
            }
        #we can use same name for nested function more than once 
        #but we can not call more than one of the parent function
        }
    }

/****************************************************************
 * TO CALL A INNER FUNCTION YOU MUST CALL OUTER FUNCTIONS FIRST *
 ****************************************************************/
    #xoo();//error: as we have to declare foo() first as xoo() is nested in foo()

    function test1(){
        echo '<b>test1:</b><br>';
        foo(); //call foo()
        too();
        boo();
        too(); // we can can a function twice
        moo(); // moo() can be called as we have already called boo() and foo()
        xoo(); // xoo() can be called as we have already called boo() and foo()
        #zoo(); re-declaration error
        //we cannont call zoo() because we have already called boo() and both of them have same named nested function xoo()
    }

    function test2(){
        echo '<b>test2:</b><br>';
        foo(); //call foo()
        too();
        #moo(); 
        //we can not call moo() as the parent function boo() is not yet called
        zoo(); 
        xoo();
        #boo(); re-declaration error
        //we cannont call boo() because we have already called zoo() and both of them have same named nested function xoo()

    }

이제 test1 ()을 호출하면 출력은 다음과 같습니다.

test1:
foo() is called
foo()->too() is called
foo()->boo() is called
foo()->too() is called
foo()->boo()->moo() is called
foo()->boo()->xoo() is called

test2 ()를 호출하면 출력은 다음과 같습니다.

test2:
foo() is called
foo()->too() is called
foo()->zoo() is called
zoo()->xoo() is called

그러나 재 선언 오류를 피하기 위해 text1 ()과 test2 ()를 동시에 호출 할 수는 없습니다.


함수 이름이 임의적이고 운율이 높고 비슷한 모양의 이름이 아니라 각 함수의 고유 한 특성을 반영하면 읽고 이해하기가 더 쉽습니다. 혼란스럽고 추적하기 어렵습니다. 각각의 위치를 ​​추적하는 데 도움이되는 이름을 선택하면이 사용자 친화적이고 읽고 이해하는 데 필요한인지 부하를 줄일 수 있습니다. 나는이 게시물을 통과 할 시간이나 처벌의 의지가 없다. 비록 당신이 아마도 거기에 좋은 점이 숨겨져있을지라도, 나는 그것을 발굴하기 위해 주변에 머무르는 사람은 거의 없을 것이라고 생각한다. 읽기는 연구 프로젝트가 아닙니다. KISS
SherylHohman

0

나는 이것이 오래된 게시물이라는 것을 알고 있지만 fwiw는 기능이 로컬로만 필요할 때 재귀 호출에 깔끔하고 깔끔한 접근 방식을 제공하기 위해 중첩 된 함수를 사용합니다. 한 번 호출 됨) :

function main() {
    // Some code

    function addChildren ($parentVar) {
        // Do something
        if ($needsGrandChildren) addChildren ($childVar);
    }
    addChildren ($mainVar); // This call must be below nested func

    // Some more code
}

예를 들어 JS와 비교하여 PHP에서 주목할 점은 중첩 된 함수에 대한 호출이 함수 선언 이후에 이루어져야한다는 것입니다 (즉, 함수 선언이 부모 함수 내 어디에서나있을 수있는 JS와 비교)


0

이 특성은 더 범주적인 기본 함수 내에서 작은 재귀 함수를 실행하는 것이 유용 할 때만 실제로 사용했지만 기본 프로세스 동작의 기본이기 때문에 다른 파일로 옮기고 싶지 않았습니다. 이 작업을 수행하는 다른 "모범 사례"방법이 있다는 것을 알고 있지만, 개발자가 내 파서를 볼 때마다 해당 기능을 볼 수 있도록하고 싶습니다. 어쨌든 수정해야 할 것 같습니다.


-1

중첩 함수는 메모 화 (성능 향상을위한 캐싱 함수 결과)에서 유용합니다.

<?php
function foo($arg1, $arg2) {
    $cacheKey = "foo($arg1, $arg2)";
    if (! getCachedValue($cacheKey)) {
        function _foo($arg1, $arg2) {
            // whatever
            return $result;
        }
        $result = _foo($arg1, $arg2);
        setCachedValue($cacheKey, $result);
    }
    return getCachedValue($cacheKey);
}
?>

3
원칙적으로는이 아이디어가 마음에 들지만 인수를받는 함수에서는 작동하지 않으며 이러한 인수를 기반으로 캐싱합니다. 다른 args를 사용 하여 함수가 두 번째로 호출 되면 결과가 캐시되지 않고 재 선언 _foo()을 시도하여 치명적인 오류가 발생합니다.
MrWhite 2011-09-13

-1

중첩 함수는 중첩 함수가 부모 함수 내에서 선언 된 변수를 활용하도록하려는 경우 유용합니다.

<?php
ParentFunc();
function ParentFunc()
{
  $var = 5;
  function NestedFunc()
  {
    global $var;
    $var = $var + 5;
    return $var;
  };
  echo NestedFunc()."<br>";
  echo NestedFunc()."<br>";
  echo NestedFunc()."<br>";
}
?>

2
이것은 절대하지 말아야하는 방법입니다.
Denis V

1
@fiddler NestedFunc는 실제로 중첩되지 않았기 때문에 전역으로 이동합니다.
Denis V
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.