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
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
'source' 카테고리의 다른 글
MySQL Alter 테이블에서 오류가 발생했습니다.잘못된 NULL 값 사용 (0) | 2022.11.16 |
---|---|
Java를 사용하여 큰 텍스트 파일을 한 줄씩 읽는 방법은 무엇입니까? (0) | 2022.11.16 |
구문 오류:strict 모드에서의 const 사용 (0) | 2022.11.16 |
다양한 목록 지우기 방법 (0) | 2022.11.16 |
MySQL collation_server가 특수 문자를 구분합니다. (0) | 2022.11.16 |