Nested or inner class in PHP

I am creating a User Class for my new website, but this time I thought to build it a little differently ...

I know that C ++ , Java and even Ruby (and, possibly, other programming languages) allow nested / inner classes inside the main class, allows you to make the code more object oriented and organized.

In PHP, I would like to do something like this:

<?php public class User { public $userid; public $username; private $password; public class UserProfile { // Some code here } private class UserHistory { // Some code here } } ?> 

Is this possible in PHP? How can i achieve this?




UPDATE

If this is not possible, can future versions of PHP support nested classes?

+67
oop php class inner-classes nested
May 7 '13 at 16:39
source share
9 answers

Introduction:

Nested classes relate to other classes a little differently than to outer classes. Take Java as an example:

Non-static nested classes have access to other members of the enclosing class, even if they are declared private. In addition, non-static nested classes require an instance of the parent class to instantiate.

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

There are several good reasons to use them:

  • This is a way to logically group classes that are used in only one place.

If a class is useful only for one other class, then it is logical to link and insert it into this class and maintain them together.

  • It increases encapsulation.

Consider two top-level classes: A and B, where B requires access to members of A, which would otherwise be declared private. By hiding class B in class A, members of A can be declared private and B can access them. In addition, B himself may be hidden from the outside world.

  • Nested classes can lead to more readable and maintainable code.

A nested class usually refers to this parent class and together forms a "package"

In php

You may have similar behavior in PHP without nested classes.

If all you want to achieve is structure / organization, since Package.OuterClass.InnerClass, PHP namespaces might be sufficient. You can even declare more than one namespace in a single file (although this may not be desirable due to standard startup functions).

 namespace; class OuterClass {} namespace OuterClass; class InnerClass {} 

If you want to imitate other characteristics, such as the visibility of elements, a little more effort is required.

Class definition package

 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()"); } } } } 



Use case

 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(); } } } 

Testing

 $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) 

Output:

 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 



Note:

I really don't think trying to emulate innerClasses in PHP is such a good idea. I think the code is less clean and readable. In addition, there are probably other ways to achieve similar results using a well-established template such as the Observer template, Decorator ou COmposition. Sometimes even simple inheritance is enough.

+102
May 7 '13 at 17:17
source share

Real nested classes with public / protected / private accessibility availability were proposed in 2013 for PHP 5.6 as an RFC, but did not do this (there is no vote yet, update from 2013 to 2016/12/29):

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

 class foo { public class bar { } } 

At least anonymous classes turned it into PHP 7

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

From this RFC page:

Visibility range

The changes made by this patch mean nested classes that are easier to implement (a tiny bit).

So, we can get the nested classes in some future version, but have not decided yet.

+11
Jul 16 '15 at 12:39 on
source share

You cannot do this in PHP. However, there are functional ways to accomplish this.

For more information, please check this post: How to insert a PHP nested class or nested methods?

This implementation method is called a free interface: http://en.wikipedia.org/wiki/Fluent_interface

+8
May 7, '13 at 16:46
source share

You cannot do this in PHP. PHP supports include, but you cannot even do this inside a class definition. There are not many great options.

This does not answer your question directly, but you may be interested in "Namespaces," the awfully ugly \ syntax \ hacked \ on \ top \ PHP OOP: http://www.php.net/manual/en/language.namespaces .rationale.php

+3
May 7 '13 at 16:43
source share

With PHP version 5.4, you can force objects to be created using a private constructor through reflection. It can be used to model Java nested classes. Code example:

 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"; 
+3
Jan 04 '16 at 20:08
source share

He is waiting for a vote as RFC https://wiki.php.net/rfc/anonymous_classes

+1
Mar 19 '15 at 11:53
source share

I think I wrote an elegant solution to this problem using namespaces. In my case, the inner class does not need to know its parent class (for example, a static inner class in Java). As an example, I created a class called "User" and a subclass of "Type", which was used as a reference for custom types (ADMIN, OTHERS) in my example. Regards.

User.php (user class file)

 <?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 (example of how to call a "subclass")

 <?php require_once("User.php"); //calling a subclass reference: echo "Value of user type Admin: ".User\Type::ADMIN; ?> 
+1
Aug 22 '16 at 16:20
source share

According to Xenon's comment on Anıl Özselgin's answer, anonymous classes were implemented in PHP 7.0, which is as close as possible to the nested classes, as you will get right now. Here are the relevant RFCs:

Nested classes (status: removed)

Anonymous classes (status: implemented in PHP 7.0)

An example of the original post, here is what your code looks like:

 <?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 } } } ?> 

This, however, comes with a very nasty warning. If you are using an IDE such as PHPStorm or NetBeans, then add this method to the User class:

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

... bye bye byo auto-completion. This is true even if you code interfaces (I in SOLID) using a template like this:

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

If only your calls to $this->profile are related to the __construct() method (or some $this->profile method), then you will not get any type of hint. Your property is essentially “hidden” to your development environment, which makes life very difficult if you rely on your IDE to automatically complete, smell the code, and refactor.

+1
Jan 31 '17 at 23:14
source share

Put each class in separate files and "require" them.

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(); } } ?> 

UserProfile.php

 <?php class UserProfile { // Some code here } ?> 

UserHistory.php

 <?php class UserHistory { // Some code here } ?> 
0
Nov 24 '17 at 22:59
source share



All Articles