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 PackageMyParentPublicChild();
 //instantiate protected child inside parent
 $this->protectedChild = new PackageMyParentProtectedChild();
}
public function test() {
 echo "Call from parent -> ";
 $this->publicChild->protectedMethod();
 $this->protectedChild->protectedMethod();

echo "<br>Siblings<br>";
 $this->publicChild->callSibling($this->protectedChild);
}
} }
namespace PackageMyParent {
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 PackageMyParent(); $parent->test(); $pubChild = new PackageMyParentPublicChild();//create new public child (possible) $protChild = new PackageMyParentProtectedChild(); //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()method(또는 임의의 방법)$this->profile에 정의되어 있습니다)에서는, 어떠한 타입의 힌트도 얻을 수 없습니다.IDE에 대한 자동 완성, 코드 냄새 스니핑 및 리팩터링을 IDE에 의존하는 경우 자산이 IDE에 대해 “숨겨져” 있기 때문에 생활이 매우 어렵습니다.