Viewed   311 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

3

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.

Friday, October 28, 2022
1

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.

Sunday, October 23, 2022
5

Sounds perfectly reasonable to me. By making it an inner class, you're making it easy to find and an obvious candidate for review when the searchable class changes.

Tight coupling is only bad when you couple things that don't really belong together just because one of them happens to call the other one. For classes that collaborate closely, e.g. when, as in your case, one of them exists to support the other, then it's called "cohesion", and it's a good thing.

Saturday, August 13, 2022
 
4

In java it looks like that:

  new JButton().addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
          // code that will be performed on any action on this component
      }
  };

here ActionListener - is an interface, and by calling new ActionListener() {/*interfaces method implementations goes here*/}; you're creating anonymous class (anonymous because it has no name) - implementation of that interface.

Or you can make inner class like this:

 class MyActionListener implements ActionListener {
   public void actionPerformed(ActionEvent e) {
      // code that will be performed on any action on this component
   }
 };

and then use it like this:

 new JButton().addActionListener(new MyActionListener());

Moreover you can declare your listener as a top-level or static inner class. But using anonymous inner class sometimes is very useful because it allows you to implement your listener almost in the same place where the component which actions your listener is listening to is declared. Obviously it won't be a good idea if the listeners methods code is very long. Then it would be better to move it into a non-anonymous inner or static nested or top-level class.

In general, innner classes are non-static classes that somehow resides inside the body of the top-level class. Here you can see examples of them in Java:

//File TopClass.java
class TopClass {
    class InnerClass {
    }
    static class StaticNestedClass {
    }
    interface Fooable {
    }   
    public void foo() {
        new Fooable(){}; //anonymous class
        class LocalClass { 
        }
    }
    public static void main(String... args) {
        new TopClass();
    }
}
Wednesday, September 28, 2022
 
stefg
 
1

You can make the inner class package private which means that it will only be accessible from other classes in exactly the same package. This is also done quite frequently for hidden classes inside the standard JDK packages like java.lang or java.util.

in pkg/MyClass.java

public class MyClass {
  ...
}

in pkg/MyHiddenClass.java

class MyHiddenClass {

  final MyClass outer;

  MyHiddenClass( MyClass outer )
  {
      this.outer = outer;
  }
  ...
}

Now when you want to access methods or variables of the outer class you need to prefix them with outer. but you get essentially the same functionality as before when the reference to the outer instance was synthetically created by the compiler.

Friday, November 18, 2022
 
Only authorized users can answer the search term. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :