Asked  7 Months ago    Answers:  5   Viewed   55 times

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?

 Answers

23

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

Testing

$parent = new PackageMyParent();
$parent->test();
$pubChild = new PackageMyParentPublicChild();//create new public child (possible)
$protChild = new PackageMyParentProtectedChild(); //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.

Wednesday, March 31, 2021
 
drowneath
answered 7 Months ago
41

Yes, you can create both a nested class or an inner class inside a Java interface (note that contrarily to popular belief there's no such thing as an "static inner class": this simply makes no sense, there's nothing "inner" and no "outter" class when a nested class is static, so it cannot be "static inner").

Anyway, the following compiles fine:

public interface A {
    class B {
    }
}

I've seen it used to put some kind of "contract checker" directly in the interface definition (well, in the class nested in the interface, that can have static methods, contrarily to the interface itself, which can't). Looking like this if I recall correctly.

public interface A {
    static class B {
        public static boolean verifyState( A a ) {
            return (true if object implementing class A looks to be in a valid state)
        }
    }
}

Note that I'm not commenting on the usefulness of such a thing, I'm simply answering your question: it can be done and this is one kind of use I've seen made of it.

Now I won't comment on the usefulness of such a construct and from I've seen: I've seen it, but it's not a very common construct.

200KLOC codebase here where this happens exactly zero time (but then we've got a lot of other things that we consider bad practices that happen exactly zero time too that other people would find perfectly normal so...).

Wednesday, June 2, 2021
 
sohum
answered 5 Months ago
66

Static inner classes are mostly similar to top-level classes, except the inner class has access to all the static variables and methods of the enclosing class. The enclosing class name is effectively appended to the package namespace of the inner class. By declaring a class as a static inner class, you are communicating that the class is somehow inseparably tied to the context of the enclosing class.

Non-static inner classes are less common. The main difference is that instances of a non-static inner class contain an implicit reference to an instance of the enclosing class, and as a result have access to instance variables and methods of that enclosing class instance. This leads to some odd looking instantiation idioms, for example:

Levels levels = new Levels(); // first need an instance of the enclosing class

// The items object contains an implicit reference to the levels object
Levels.Items items  = levels.new Items(); 

Non-static inner classes are much more intimately tied to their enclosing classes than static inner classes. They have valid uses (for example iterators are often implemented as non-static inner classes within the class of the data structure they iterate over).

It's a common mistake to declare a non-static inner class when you only really need the static inner class behaviour.

Wednesday, July 28, 2021
 
Puneet
answered 3 Months ago
10

An inner class needs a reference to an instance of the outer class in order to be constructed. If your class doesn't logically need that, then use the static modifer to make it "just a nested class":

public static class Class2 {
    public void newMethod(){
        System.out.println("Second class");
    }
}

EDIT: To create an instance of Class2 as an inner class, you could use something like:

Class1 outer = new Class1();
Class2 myObject = outer.new Class2();

Or more briefly:

Class2 myObject = new Class1().new Class2();

... but unless you really want a reference to an enclosing instance, it's much simpler to make the class just a nested class.

Friday, July 30, 2021
 
adjco
answered 3 Months ago
28

This is the problem:

class MyList<T> implements Iterable<T> {
    private class MyListIterator<T> implements Iterator<T> {
        ...
    }
}

(It doesn't help that in your cut down version you've made MyList non-generic.)

At that point there are two different T type variables - the one in the nested class and the one in the outer class. You don't need Node to be generic - you just need:

class MyList<T> implements Iterable<T> {
    private class MyListIterator implements Iterator<T> {
        ...
    }
}

Now there's only one T - the one in the outer class. It's not like you want the list iterator to have a different T from the one declared in the enclosing instance, so you don't want it to be generic.

To put it another way: try making MyListIterator generic in a type parameter with a different name, and then it'll be clearer what's going wrong, as the two names will be distinguishable in the error message. It's effectively:

Type mismatch: cannot convert from another.main.MyList.Node<TOuter> to
another.main.MyList.Node<TInner>

(or vice versa).

Wednesday, September 29, 2021
 
huhushow
answered 3 Weeks ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :