Nested or Inner Class in PHP

PhpClassOopNestedInner Classes

Php Problem Overview


I'm building a User Class for my new website, however this time I was thinking to build it little bit differently...

C++, Java and even Ruby (and probably other programming languages) are allowing the use of nested/inner classes inside the main class, which allows us to make the code more object-oriented and organized.

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

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

    public class UserProfile {
      // some code here
    }

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

Is that possible in PHP? How can I achieve it?


UPDATE

If it's impossible, will future PHP versions might support nested classes?

Php Solutions


Solution 1 - Php

Intro:

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

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

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

There are several compelling reasons for using them:

  • It is a way of logically grouping classes that are only used in one place.

> If a class is useful to only one other class, then it is logical to > relate and embed it in that class and keep the two together.

  • It increases encapsulation.

> Consider two top-level classes, A and B, where B needs access to > members of A that would otherwise be declared private. By hiding class > B within class A, A's members can be declared private and B can access > them. In addition, B itself can be hidden from the outside world.

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

> A nested class usually relates to it's parent class and together form a "package"

In PHP

You can have similar behavior in PHP without nested classes.

If all you want to achieve is structure/organization, as Package.OuterClass.InnerClass, PHP namespaces might sufice. You can even declare more than one namespace in the same file (although, due to standard autoloading features, that might not be advisable).

namespace;
class OuterClass {}

namespace OuterClass;
class InnerClass {}

If you desire to emulate other characteristics, such as member visibility, it takes a little more effort.

Defining the "package" class

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. Also, there are probably other ways to achieve similar results using a well established pattern such as the Observer, Decorator ou COmposition Pattern. Sometimes, even simple inheritance is sufficient.

Solution 2 - Php

Real nested classes with public/protected/private accessibility were proposed in 2013 for PHP 5.6 as an RFC but did not make it (No voting yet, no update since 2013 - as of 2021/02/03):

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

class foo {
    public class bar {
 
    }
}

At least, anonymous classes made it into PHP 7

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

From this RFC page:

> ### Future Scope > > The changes made by this patch mean named nested classes are easier to implement (by a tiny bit).

So, we might get nested classes in some future version, but it's not decided yet.

Solution 3 - Php

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

For more details please check this post: https://stackoverflow.com/questions/533459/how-to-do-a-php-nested-class-or-nested-methods

This way of implementation is called fluent interface: http://en.wikipedia.org/wiki/Fluent_interface

Solution 4 - Php

As per Xenon's comment to Anıl Özselgin's answer, anonymous classes have been implemented in PHP 7.0, which is as close to nested classes as you'll get right now. Here are the relevant RFCs:

Nested Classes (status: withdrawn)

Anonymous Classes (status: implemented in PHP 7.0)

An example to the original post, this is what your code would look 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, though, comes with a very nasty caveat. If you use an IDE such as PHPStorm or NetBeans, and then add a method like this to the User class:

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

...bye bye auto-completion. This is the case even if you code to interfaces (the I in SOLID), using a pattern like this:

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

Unless your only calls to $this->profile are from the __construct() method (or whatever method $this->profile is defined in) then you won't get any sort of type hinting. Your property is essentially "hidden" to your IDE, making life very hard if you rely on your IDE for auto-completion, code smell sniffing, and refactoring.

Solution 5 - Php

Since PHP version 5.4 you can force create objects with private constructor through reflection. It can be used to simulate Java nested classes. Example code:

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";

Solution 6 - Php

You can, like this, in 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)();

Solution 7 - Php

You can't do it in PHP. PHP supports "include", but you can't even do that inside of a class definition. Not a lot of great options here.

This doesn't answer your question directly, but you may be interested in "Namespaces", a terribly ugly\syntax\hacked\on\top\of PHP OOP: http://www.php.net/manual/en/language.namespaces.rationale.php

Solution 8 - Php

It is waiting for voting as RFC https://wiki.php.net/rfc/anonymous_classes

Solution 9 - Php

I think I wrote an elegant solution to this problem by using namespaces. In my case, the inner class does not need to know his parent class (like the static inner class in Java). As an example I made a class called 'User' and a subclass called 'Type', used as a reference for the user 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 (An example of how to call the 'subclass')

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

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

Solution 10 - Php

From PHP 8.1, it is even better. Combining Anonymous classes and New in initializers features, you get something like this:

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

    public function __construct()
    {}
}

The good point is, unlike the PHP7 days (as answered by e_i_pi), you don't need to define the class in the constructor. This way, (1) the constructor doesn't get polluted, and (2) in the case of many nested classes (or even one), you don't have things over-indented (i.e. you don't double the indentation when you define a new nested class). Pretty cool.

Wait... But what if you want to instantiate a class more than once? Well, let's combine things more: You can take Arrow functions into the action (well, including other new features as its sauce, like Constructor property promotion, Enumerations and Readonly properties):

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

Impresive! Yes, but it has one main disadvantage: You cannot use the type of anonymous classes (e.g. look at UserHistory::add()). There might be other problems as well, like the auto-completion feature provided by editors not working perfectly. However, live with it; as you often loose something when you achieve something else.

Solution 11 - Php

This page keeps coming up in my Internet searches on this subject so figured I should chime in even though this is an 8-year old post. The documentation for PHP5 demonstrates that anonymous classes can be defined within a class method. The object created can extend, implement, and even use other classes, interfaces, and traits. Consider the following OOP paradigm of factory object production. Similar to what @e-i-pi pointed out ...

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!"

The objects are one-off, so one would expect the static values of the objects returned would not bind from one instance to another. After all, the anonymous class is unique from one object to another. However, late static binding works as one would otherwise expect from a nested class.

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

So, there you go: inner/nested classes and creation of their objects with static functionality has been possible since September 22, 2013 (right about the time this question was asked).

Solution 12 - Php

Put each class into 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
    }

?>

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionLior ElromView Question on Stackoverflow
Solution 1 - PhpTivieView Answer on Stackoverflow
Solution 2 - PhpFabian SchmenglerView Answer on Stackoverflow
Solution 3 - PhpSumoanandView Answer on Stackoverflow
Solution 4 - Phpe_i_piView Answer on Stackoverflow
Solution 5 - PhpPascal9xView Answer on Stackoverflow
Solution 6 - PhpArlon ArriolaView Answer on Stackoverflow
Solution 7 - PhpdkaminsView Answer on Stackoverflow
Solution 8 - PhpAnıl ÖzselginView Answer on Stackoverflow
Solution 9 - PhpRogerio SouzaView Answer on Stackoverflow
Solution 10 - PhpMAChitgarhaView Answer on Stackoverflow
Solution 11 - PhpMort 1305View Answer on Stackoverflow
Solution 12 - PhppriyabagusView Answer on Stackoverflow