Nested or Inner Class in PHP

Nested or Inner Class in 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.

(c#) Are nested (inner) classes the only way to restrict class access to one class?

Each class that can hold multiple instances of other classes does so with a list element (e.g. FundamentalNamingConvention stores many UnitTypes in a List). Is this a proper way of doing it?

Yes - it is fine. Most likely any ICollection, IList or IEnumerable would work. But List is fine. Do note: List does allow you to add elements as well. This is sometimes undesired.

All classes (FundamentalNamingConvention, UnitType, UnitName) are only to be accessed by the NameGenerator class. To ensure this, I could simply make all these classes be inner classes of the NameGenerator but then that file would be several hundred lines of code long.

Is there a way to have the classes stored in their separate files (e.g. UnitType.cs) but still only be accessible by the NameGenerator?

Yes, you can use a partial class to split the files. E.g.:

//file1.cs
public partial class Foo
{
//normal definition

}
//file1.cs
public partial class Foo
{
private class InnerBar
{
//private inner class definition
}
}

How to do a PHP nested class or nested methods?

Have the methods return objects with the methods described, and you get what you are after.

So, as long as $DB is an object that has a comments()-method, that part is valid. If that comments() returns an object that has an id()-method, that part is valid, too. Then, id() needs to return an object that has the limit()-method.

In your particular case, you might want to do something like this:

class DB {
public function comments() {
// do preparations that make the object select the "comments"-table...
return $this;
}

public function id($string) {
// handle this too...
return $this;
}

public function limit($int) {
// also this
return $this;
}

public function execute() {
$success = try_to_execute_accumulated_db_commands();
return $success;
}
}

$DB = new DB();
$DB->comments()->id(" > 3")->limit(10);

In my example, every method (also not depicted here) would return the object itself, so that commands can be chained together. When the construction of the database query is done, you actually evaluate the query by invoking execute() that (in my case) would return a boolean that would represent the success of the database execution.

User nickohm suggested that this is called a fluent interface. I must admit that this is a new term for me, but that tells probably more of my knowledge, than the term's usage. ("I just write code, you know...")

Note: $this is a 'magic' variable that points to the currently active object. As the name suggests, it just returns itself as the return value for the method.

PHP How to instance an inner class

If you're trying to use the inner class as a namespace, just use namespaces. Ex:

<?php
namespace Pages;
class Page { }

Then you can access the class through:

$g_Pages = new \Pages\Page("My Name", "My Title");

http://php.net/namespaces

Accessing a class variable from within a nested class

Assuming you had a proper inheritance model set up, you could use parent::. But your code as-is is a flat-out syntax error. You cannot nest classes like that.

Class Core {
public $var = 'test';
}

Class SubClass Extends Core {
function foo() {
$localvar = parent::$var;
}
}

comment followup:

Perhaps something more like this?

class Core {
public $Variable = 'foo';
function __construct() {
$this->subclass = new SubClass($this->variable);
}
}

class SubClass {
public $coreVariable;
function __construct(&$var) {
$this->coreVariable = $var;
}
}

Private nested classes - are they necessary for composition?

In wikipedia, there's also the following example of composition without using private nested classes.

class University
{
std::vector<Department> faculty; //this is composition
std::vector<People*> people; //this is aggregation

University() // constructor
{
// Composition: Departments exist as long as the University exists
faculty.push_back(Department("chemistry"));
faculty.push_back(Department("physics"));
faculty.push_back(Department("arts"));
}
};

For a true composition we don't have to make the whole class private as long as we treat instances of departments appropriately, i.e. we need to make sure that all departments will actually get deleted when the university ceases to exist.

An analogous ways of implementing different compositions in JavaScript would be as follows:

/*this is a component's "class"*/
function Text(txt){
this.txt = txt;
}

/*this is a composite's "class"*/
function Hello(begining){
/*public object*/
this.begining = begining;

/*this is like making an instance from a nested function which is a composite*/
var Middle = new (function Middle(txt){
this.txt = txt;
})(" - ");

/*private object - also a composite, even though it's made of the public class Text*/
var Ending = new Text("that's all.");

/*The use of the private property Ending*/
this.Say = function(){
var msg = this.begining.txt + Middle.txt + Ending.txt;
console.log(msg);
}
}
/*
This txt variable will be the "begining" text. It could still be a composite, but
only if it's not used in another composite when purpose is to be
"an untransferable part" and not a "visitor".
*/
var txt = new Text("Dan");
var say1 = new Hello(txt);
var say2 = new Hello(new Text("To be or not to be"));

say1.Say() /*Dan - that's all.*/
say2.Say() /*To be or not to be - that's all.*/

But even the transferability is often a neglected rule when people see "cars" having "wheels" as parts (rather then "visitors")

Instead of perhaps "neural network" and "neurons" or "forest" and "trees". Trees don't get replanted to a different forest so often.

And since the wheels can still be understood as parts of a composite, it doesn't have to differ from aggregation in code.



Related Topics



Leave a reply



Submit