php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Request #79720 Nested classes like in Java
Submitted: 2020-06-21 16:34 UTC Modified: 2020-06-22 11:53 UTC
From: 6562680 at gmail dot com Assigned:
Status: Suspended Package: Class/Object related
PHP Version: 7.3.19 OS: Win10
Private report: No CVE-ID: None
Have you experienced this issue?
Rate the importance of this bug to you:

 [2020-06-21 16:34 UTC] 6562680 at gmail dot com
Description:
------------
Am using \Closure with bind() to pass protected variables into instances from factory.

As i remember in Java we can define sharing this context to child classes.

Why it was removed?

```
Class A
{
  public function newChildA()
  {
    $a = new ChildA;
    $a->value = 123;
  }

  Class ChildA
  {
    protected $value;
  }
}
```

Otherwise we should add setters, and somebody of course use it directly in his code.

Its about "readonly" property, not just "protected".

Main problem is "no internal dependency injector", we can pass attributes via __construct... What if dependencies? We still need to create builder that should pass parameters to constructor with autowired dependencies, and then we lost possibility to create object via "new" operator, otherwise we should add container/di into each class as dependency.

Then we should remember base class dependency when extending and that stuff.

How do you think?

Test script:
---------------
Class A
{
  public function newChildA($value)
  {
    $func = function ($value) {
      $this->value = $value;
    };

    $a = new ChildA;
    $func->call($a, 123);
  }
}

Class ChildA
{
  protected $value;
}


Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2020-06-21 20:00 UTC] requinix@php.net
-Status: Open +Status: Suspended -Package: *General Issues +Package: Class/Object related
 [2020-06-21 20:00 UTC] requinix@php.net
*I* think there are already well-established OOP principles that make child classes irrelevant.

But regardless of what I think, a significant change like this requires the RFC process.
https://wiki.php.net/rfc/howto

But note that you're not the first to propose this:

A few years ago there was an RFC about nested classes. It was not completed.
https://wiki.php.net/rfc/nested_classes

The related concept of friend classes was submitted a bit more recently and declined.
https://wiki.php.net/rfc/friend-classes
 [2020-06-22 11:53 UTC] 6562680 at gmail dot com
If we speaking about javascript where we have internal dependency injector - we could say about oop principles.

oop has a point: object has state and behavior. if we need to change the object from the code - we implement more methods to change. if methods are public - anybody could use internal objects public methods.

I just little disappointed about "solid" principles with PHP - where we need to create tonns of public methods, that of course, could be used outside the library and possible create problems.

to inject dependencies we have to create factory/builder and pass container as a dependency in this builder. Then we could create object and even then we need public methods or bound callbacks to fill protected properties.

There are breaking incapsulation. First we declare property protected, then we create public methods - same as we declare property "public". Because we need to separate opportunity of injection/creating in different class, opportunity to control values - in second class, opportunity to manage control values - in third class.

Otherwise we have mega-class with tonns of methods. We can separate it to traits simulating multiple-extends, and for my points - there is something wrong in oop principles implementation.

Maybe oop was created for multiple extends, that causes many hardcoded objects and makes future changes harder or impossible, especially if we talk about complete composer library, that cannot be changed, and only decorated or replaced.

Change-ability has strong dependency from injector with interface-bound stuff. Otherwise you cannot remove the module. In same time passing arguments to protected properties requires constructor (used for dependencies), public methods - could be used by client where i didnt expect that, or bound callbacks - that looks like oop-functional combination.

For example - you want to store messages in collection, and create class "inspection" that do some job, and then fill this collection

class InspectionBuilder
{
  protected $container;


  public function __construct(PsrContainerInterface $container)
  {
    $this->container = $container;
  }


  // hello Java constructor overload
  // but you right, if create constructor overloading it wouldn't help anything
  // because class to create himself should contain DI as dependency
  // there we could create Singletons and start to shit-coding with statics instead of interfaces, because of PHP nature so
  // We'll get a lock where we need self to build self. So builder - its the right way.

  public function newInspectionFromSomething()
  {
    // damn, constructor requires arguments and couldn't be created
    // return $this->container->get(Inspection::class);
    // so we create own interface with method make()

    $messagesCollection = $this->container->get(MessagesCollection::class);

    $inspection = $this->container->make(Inspection::class, $someRequiredData = [123]);
    $inspection->setMessagesCollection($messagesCollection);

    // finally we have inspection...
    
    return $inspection;
  }
}

Class Inspection
{
  protected MessagesCollection $messagesCollection;

  protected mixed $someRequiredData;

  public function __construct(array $someRequiredData)
  {
    // now we can new Inspection($data), but we still havent dependencies then
    // hmm, make __construct(MessagesCollection $collection, $someRequiredData)
    // but really it will be needed only when we start work with messages
    // hmm, possible we have to create setMessagesCollection() method

    $this->someRequiredData = $someRequiredData;
  }

  public function setMessagesCollection(MessagesCollection $messagesCollection)
  {
    $this->messagesCollection = $messagesCollection;
  }
}

Class MessagesCollection
{
  protected array $messages;
}
```

Next question is - how to add messages into this protected collection?
Create getter for collection? End-user will modify it and then forget where he did that.
So there should be possibility to work with single this outside functional way.

You know, alternative is to implement all of the methods by inspection, validation, message compilation and building inside one Inspection class
First of all class becomes mega-size. Second - will break dependency-inversion where we can replace custom behavior adding dependency by existing interface.

Something wents wrong in "oop implementation";

Functional programming provides import/export like ours "require/return" but then autoloader wont work. Again the deadlock.
Java added nested classes and { get,set } syntax to reduce class size, makes it easier to read and manage.

We should code on PHP style using functional style to make things, because of RFC denied. Should think about it.

Classes should looks like:

```
Class A
{}

return [ 'a' => new A ];
```

and combine runtime/declaration in one file.
 
PHP Copyright © 2001-2020 The PHP Group
All rights reserved.
Last updated: Sun Oct 25 16:01:24 2020 UTC