source

PHP의 중첩 또는 내부 클래스

factcode 2022. 11. 16. 21:24
반응형

PHP의 중첩 또는 내부 클래스

새로운 웹 사이트의 사용자 클래스를 만들고 있습니다만, 이번에는 조금 다른 방법으로 구축하려고 생각하고 있습니다.

C++, Java, 그리고 Ruby(그리고 아마도 다른 프로그래밍 언어)는 메인 클래스 내에서 네스트된/내부 클래스를 사용할 수 있도록 허용하고 있습니다.이것에 의해, 우리는 코드를 보다 오브젝트 지향적이고 조직적인 것으로 만들 수 있습니다.

PHP에서는 다음과 같은 작업을 하고 싶습니다.

<?php
  public class User {
    public $userid;
    public $username;
    private $password;

    public class UserProfile {
      // some code here
    }

    private class UserHistory {
      // some code here
    }
  }
?>

그게 PHP로 가능한가요?어떻게 하면 좋을까요?


갱신하다

만약 불가능할 경우 향후 PHP 버전은 중첩된 클래스를 지원할 수 있습니까?

도입부:

네스트된 클래스는 외부 클래스와는 조금 다르게 다른 클래스와 관련됩니다.Java를 예로 들어 보겠습니다.

비정적 중첩 클래스는 비공개로 선언된 경우에도 엔클로징 클래스의 다른 멤버에 액세스할 수 있습니다.또한 비정적 네스트클래스의 경우 부모 클래스의 인스턴스를 인스턴스화해야 합니다.

OuterClass outerObj = new OuterClass(arguments);
outerObj.InnerClass innerObj = outerObj.new InnerClass(arguments);

이러한 기능을 사용하는 데는 다음과 같은 몇 가지 설득력 있는 이유가 있습니다.

  • 이는 한 곳에서만 사용되는 클래스를 논리적으로 그룹화하는 방법입니다.

클래스가 다른 하나의 클래스에만 유용한 경우 해당 클래스에 관련지어 포함시키고 두 클래스를 함께 유지하는 것이 논리적입니다.

  • 캡슐화가 향상됩니다.

A와 B의 두 가지 최상위 클래스(A와 B)를 고려합니다. 여기서 B는 A의 멤버에 액세스해야 하며, 그렇지 않으면 비공개로 선언될 수 있습니다.클래스 A 내에서 클래스 B를 숨김으로써 A의 멤버를 비공개로 선언하고 B를 액세스 할 수 있습니다.또, B 자체를 외부로부터 숨길 수 있다.

  • 클래스가 중첩되면 코드를 더 잘 읽고 유지 관리할 수 있습니다.

네스트된 클래스는 보통 부모 클래스와 관련되며 함께 "패키지"를 형성합니다.

PHP의 경우

중첩된 클래스 없이 PHP에서도 유사한 동작을 할 수 있습니다.

원하는 것이 구조/조직뿐이라면 패키지로.아우터 클래스InnerClass, PHP 네임스페이스가 필요할 수 있습니다.한 파일에 여러 개의 네임스페이스를 선언할 수도 있습니다(단, 표준 자동 로딩 기능으로 인해 권장되지 않을 수도 있습니다).

namespace;
class OuterClass {}

namespace OuterClass;
class InnerClass {}

멤버의 가시성 등 다른 특성을 에뮬레이트하려면 조금 더 많은 노력이 필요합니다.

'패키지' 클래스의 정의

namespace {

    class Package {

        /* protect constructor so that objects can't be instantiated from outside
         * Since all classes inherit from Package class, they can instantiate eachother
         * simulating protected InnerClasses
         */
        protected function __construct() {}

        /* This magic method is called everytime an inaccessible method is called 
         * (either by visibility contrains or it doesn't exist)
         * Here we are simulating shared protected methods across "package" classes
         * This method is inherited by all child classes of Package 
         */
        public function __call($method, $args) {

            //class name
            $class = get_class($this);

            /* we check if a method exists, if not we throw an exception 
             * similar to the default error
             */
            if (method_exists($this, $method)) {

                /* The method exists so now we want to know if the 
                 * caller is a child of our Package class. If not we throw an exception
                 * Note: This is a kind of a dirty way of finding out who's
                 * calling the method by using debug_backtrace and reflection 
                 */
                $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3);
                if (isset($trace[2])) {
                    $ref = new ReflectionClass($trace[2]['class']);
                    if ($ref->isSubclassOf(__CLASS__)) {
                        return $this->$method($args);
                    }
                }
                throw new \Exception("Call to private method $class::$method()");
            } else {
                throw new \Exception("Call to undefined method $class::$method()");
            }
        }
    }
}

사용 사례

namespace Package {
    class MyParent extends \Package {
        public $publicChild;
        protected $protectedChild;

        public function __construct() {
            //instantiate public child inside parent
            $this->publicChild = new \Package\MyParent\PublicChild();
            //instantiate protected child inside parent
            $this->protectedChild = new \Package\MyParent\ProtectedChild();
        }

        public function test() {
            echo "Call from parent -> ";
            $this->publicChild->protectedMethod();
            $this->protectedChild->protectedMethod();

            echo "<br>Siblings<br>";
            $this->publicChild->callSibling($this->protectedChild);
        }
    }
}

namespace Package\MyParent
{
    class PublicChild extends \Package {
        //Makes the constructor public, hence callable from outside 
        public function __construct() {}
        protected function protectedMethod() {
            echo "I'm ".get_class($this)." protected method<br>";
        }

        protected function callSibling($sibling) {
            echo "Call from " . get_class($this) . " -> ";
            $sibling->protectedMethod();
        }
    }
    class ProtectedChild extends \Package { 
        protected function protectedMethod() {
            echo "I'm ".get_class($this)." protected method<br>";
        }

        protected function callSibling($sibling) {
            echo "Call from " . get_class($this) . " -> ";
            $sibling->protectedMethod();
        }
    }
}

테스트

$parent = new Package\MyParent();
$parent->test();
$pubChild = new Package\MyParent\PublicChild();//create new public child (possible)
$protChild = new Package\MyParent\ProtectedChild(); //create new protected child (ERROR)

출력:

Call from parent -> I'm Package protected method
I'm Package protected method

Siblings
Call from Package -> I'm Package protected method
Fatal error: Call to protected Package::__construct() from invalid context

주의:

PHP에서 innerClasses를 에뮬레이트하는 것은 그다지 좋은 생각이 아니라고 생각합니다.코드가 덜 깨끗하고 읽기 쉬운 것 같아요.또한 Observer, Decorator u Coomposition Pattern 등 확립된 패턴을 사용하여 유사한 결과를 얻을 수 있는 다른 방법이 있을 수 있습니다.때로는 단순한 상속으로도 충분합니다.

「」를 포함한 클래스:public/protected/private접근성은 2013년에 PHP 5.6에 대해 RFC로 제안되었지만 실현되지 않았습니다(아직 투표 없음, 2013년 이후 업데이트 없음 - 2021/02/03 현재).

https://wiki.php.net/rfc/nested_classes

class foo {
    public class bar {
 
    }
}

적어도 익명 클래스는 PHP 7에 포함되었습니다.

https://wiki.php.net/rfc/anonymous_classes

이 RFC 페이지부터:

장래의 범위

네스트 클래스라는 이름의 이 패치 평균에 의해 이루어진 변경은 구현하기 쉬워집니다(조금만).

따라서 향후 버전에서 중첩된 클래스가 제공될 수 있지만 아직 결정되지 않았습니다.

PHP에서는 이 작업을 수행할 수 없습니다.다만, 이것을 실현하는 기능적인 방법이 있습니다.

상세한 것에 대하여는, 다음의 투고를 참조해 주세요.PHP 중첩된 클래스 또는 중첩된 메서드를 수행하는 방법

이 실장 방법을 fluent interface라고 부릅니다.http://en.wikipedia.org/wiki/Fluent_interface

PHP 버전 5.4부터 리플렉션을 통해 프라이빗 컨스트럭터로 오브젝트를 강제로 작성할 수 있습니다.Java 중첩 클래스를 시뮬레이션하는 데 사용할 수 있습니다.코드 예:

class OuterClass {
  private $name;

  public function __construct($name) {
    $this->name = $name;
  }

  public function getName() {
    return $this->name;
  }

  public function forkInnerObject($name) {
    $class = new ReflectionClass('InnerClass');
    $constructor = $class->getConstructor();
    $constructor->setAccessible(true);
    $innerObject = $class->newInstanceWithoutConstructor(); // This method appeared in PHP 5.4
    $constructor->invoke($innerObject, $this, $name);
    return $innerObject;
  }
}

class InnerClass {
  private $parentObject;
  private $name;

  private function __construct(OuterClass $parentObject, $name) {
    $this->parentObject = $parentObject;
    $this->name = $name;
  }

  public function getName() {
    return $this->name;
  }

  public function getParent() {
    return $this->parentObject;
  }
}

$outerObject = new OuterClass('This is an outer object');
//$innerObject = new InnerClass($outerObject, 'You cannot do it');
$innerObject = $outerObject->forkInnerObject('This is an inner object');
echo $innerObject->getName() . "\n";
echo $innerObject->getParent()->getName() . "\n";

Anyl Ozselgin의 답변에 대한 제논의 코멘트에 따르면 익명의 클래스는 PHP 7.0으로 구현되어 있으며, 이는 지금 바로 얻을 수 있는 네스트된 클래스에 가깝습니다.관련된 RFC는 다음과 같습니다.

중첩된 클래스(상태: 철회됨)

익명 클래스(상태: PHP 7.0에 구현됨)

원래의 투고의 예로서, 코드는 다음과 같습니다.

<?php
    public class User {
        public $userid;
        public $username;
        private $password;

        public $profile;
        public $history;

        public function __construct() {
            $this->profile = new class {
                // Some code here for user profile
            }

            $this->history = new class {
                // Some code here for user history
            }
        }
    }
?>

그러나 이것은 매우 불쾌한 경고를 수반한다. 를 하고, PHPStorm 의 NetBeans 의 IDE 에 하는 경우, 「」, 「」, 「」, 「」, 「」, 「」, 「」, 「」, 「」를 참조해 주세요.User 링크:

public function foo() {
  $this->profile->...
}

...바이바이 자동발사.이것은, 다음과 같은 패턴을 사용해 인터페이스(SOLID 의 I)에 코드를 붙이는 경우에서도 마찬가지입니다.

<?php
    public class User {
        public $profile;

        public function __construct() {
            $this->profile = new class implements UserProfileInterface {
                // Some code here for user profile
            }
        }
    }
?>

의 한$this->profile에서 왔습니다.__construct()의 방식)$this->profile에 정의되어 있습니다)에서는, 어떠한 타입의 힌트도 얻을 수 없습니다.IDE에 대한 자동 완성, 코드 냄새 스니핑 및 리팩터링을 IDE에 의존하는 경우 자산이 IDE에 대해 "숨겨져" 있기 때문에 생활이 매우 어렵습니다.

PHP 7에서는 다음과 같이 할 수 있습니다.

class User{
  public $id;
  public $name;
  public $password;
  public $Profile;
  public $History;  /*  (optional declaration, if it isn't public)  */
  public function __construct($id,$name,$password){
    $this->id=$id;
    $this->name=$name;
    $this->name=$name;
    $this->Profile=(object)[
        'get'=>function(){
          return 'Name: '.$this->name.''.(($this->History->get)());
        }
      ];
    $this->History=(object)[
        'get'=>function(){
          return ' History: '.(($this->History->track)());
        }
        ,'track'=>function(){
          return (lcg_value()>0.5?'good':'bad');
        }
      ];
  }
}
echo ((new User(0,'Lior','nyh'))->Profile->get)();

PHP에서는 할 수 없습니다.PHP는 "포함"을 지원하지만 클래스 정의 내부에서는 이를 수행할 수 없습니다.여기엔 좋은 선택지가 별로 없어요.

이것은 당신의 질문에 직접 답하는 것은 아니지만, 당신은 PHP OOP의 매우 추악한 "네임스페이스"에 관심이 있을지도 모릅니다.http://www.php.net/manual/en/language.namespaces.rationale.php

RFC https://wiki.php.net/rfc/anonymous_classes 로서 투표를 기다리고 있습니다.

나는 네임스페이스를 사용하여 이 문제에 대한 우아한 해결책을 썼다고 생각한다.이 경우 inner 클래스는 부모 클래스(Java의 정적 inner 클래스 등)를 알 필요가 없습니다.예를 들어 'User'라는 클래스와 'Type'이라는 서브클래스를 만들어 사용자 유형(ADMIN, OTHTHER)에 대한 참조로 사용했습니다.안부 전해요.

User.php(사용자 클래스 파일)

<?php
namespace
{   
    class User
    {
        private $type;

        public function getType(){ return $this->type;}
        public function setType($type){ $this->type = $type;}
    }
}

namespace User
{
    class Type
    {
        const ADMIN = 0;
        const OTHERS = 1;
    }
}
?>

Using.php('서브클래스'를 호출하는 방법의 예)

<?php
    require_once("User.php");

    //calling a subclass reference:
    echo "Value of user type Admin: ".User\Type::ADMIN;
?>

PHP 8.1부터는 더 좋습니다.Anonymous 클래스와 이니셜라이저의 New 기능을 조합하면 다음과 같은 결과를 얻을 수 있습니다.

class User
{
    public $profile = new class {
        // Some code here
    };
    private $history = new class {
        // Some code here
    };

    public function __construct()
    {}
}

좋은 점은 (e_i_pi에서 응답한) PHP7일과는 달리 컨스트럭터에서 클래스를 정의할 필요가 없다는 것입니다.이렇게 하면 (1) 컨스트럭터가 오염되지 않고 (2) 중첩된 클래스가 많은 경우(또는 하나라도) 과도하게 삽입되지 않습니다(즉, 새 중첩된 클래스를 정의할 때 들여쓰기가 두 배로 증가하지 않습니다).꽤 멋있다.

잠깐... 하지만 클래스를 두 번 이상 인스턴스화하려면 어떻게 해야 하나요?자, 더 많은 것을 조합해 봅시다.Arrow 함수(Constructor 속성 승격, Enumerations 및 Readonly 속성 등 다른 새로운 기능을 소스로 포함):

class UserHistory
{
    public $newAction = fn(...$args) => new class(...$args) {
        public function __construct(
            // Think ActionType a defined enumeration
            public readonly ActionType $type,
            public readonly string $description = '',
        ) {}
    };

    private array $actions = [];

    public function add($action)
    {
        $this->actions[] = $action;
    }
}

// Test
$userHistory = new UserHistory();
$userHistory->add(
    // Again, suppose ActionType enumeration is defined
    $userHistory->newAction(ActionType::CREATE_ACCOUNT)
);

감동적이네요!네, 단점은 다음과 같습니다.익명 클래스 유형을 사용할 수 없습니다(예:UserHistory::add()편집자가 제공하는 자동 완성 기능이 완벽하게 작동하지 않는 등 다른 문제도 있을 수 있습니다.하지만, 여러분이 다른 것을 성취했을 때 종종 무언가를 놓치므로, 그것을 참고 살아라.

이 페이지는 이 주제에 대한 인터넷 검색에서 계속 올라오고 있기 때문에 8년 된 게시물임에도 불구하고 참여해야겠다고 생각했습니다.PHP5에 대한 설명서는 클래스 메서드 내에서 익명 클래스를 정의할 수 있음을 보여줍니다.생성된 개체는 다른 클래스, 인터페이스 및 특성을 확장, 구현 및 사용할 수 있습니다.공장 객체 생산의 다음과 같은 OOP 패러다임을 고려해 보십시오.@e-i-pi가 지적한 것과 같은...

class Factory {
    /**
     *  Method to manufacture an inner-class object.
     *
     *  @param  string  $args   Arguments to be passed to
     *                          the inner-class constructor.
     */
    static function manufacture_object($args) {
        /**
         *  Here's the definition of the inner-class.
         */
        return new class($args) {
            static $remembers = 'Nothing';
            private $args;
            function __construct($args) {
                $this->$args = $args;
            }
            function says() {
                return $this->args;
            }
        };
    }
}

/**
 *  Create an inner-class object and have it do its thing.
 */
$mort = Factory::manufacture_object("Hello World!");
echo $mort->says();         // Echoes "Hello World!"

개체는 일회성이기 때문에 반환되는 개체의 정적 값이 인스턴스 간에 바인딩되지 않을 것으로 예상됩니다.어나니머스 클래스는 오브젝트마다 고유합니다.단, 레이트 스태틱바인딩은 네스트된 클래스에서 예상되는 것과 동일하게 동작합니다.

$mort = Factory::manufacture_object("I can remember that.");
$mort2 = Factory::manufacture_object("I'll live vicariously through you.");
$mort::$remembers = 'Something';
echo $mort2::$remembers;    // Echoes "Something"

자, 여기 있습니다.내부/내부 클래스와 정적 기능을 갖춘 오브젝트 작성은 2013년 9월 22일부터 가능하게 되었습니다(이 질문이 제기된 시점).

각 클래스를 개별 파일에 저장하고 "요구"합니다.

User.php

<?php

    class User {

        public $userid;
        public $username;
        private $password;
        public $profile;
        public $history;            

        public function __construct() {

            require_once('UserProfile.php');
            require_once('UserHistory.php');

            $this->profile = new UserProfile();
            $this->history = new UserHistory();

        }            

    }

?>

사용자 프로파일php

<?php

    class UserProfile 
    {
        // Some code here
    }

?>

사용자 이력php

<?php

    class UserHistory 
    {
        // Some code here
    }

?>

언급URL : https://stackoverflow.com/questions/16424257/nested-or-inner-class-in-php

반응형