[RFC] Structured Object Notation

php.internals

Midori Kocak

10 years ago
https://wiki.php.net/rfc/structured_object_notation Object Oriented Programming is a key feature to write structured programs in PHP. However, even though we have classes included in our program, the only way to create an object from a class, is to instantiate it in unstructured code. Consider this piece of code: <?php use \Psr\Http\Message\ServerRequestInterface as Request; use \Psr\Http\Message\ResponseInterface as Response; require '../vendor/autoload.php'; $app = new \Slim\App; $app->get('/hello/{name}', function (Request $request, Response $response) { $name = $request->getAttribute('name'); $response->getBody()->write("Hello, $name"); return $response; }); $app->run(); This causes loss of readability and benefits of oop in most of the programs. Most of the programs we write, end up to have some piece of spaghetti code somewhere. To prevent this, one should write a god object and run once but classes are not and should not be code blocks wrapped around “class” keyword. Here in this RFC, I propose a structured way to instantiate objects in a PHP script in a structured way. Proposal According to most definitions an object is defined as: “Real-world objects share two characteristics: They all have state and behavior.” Currently to interact with an object, one should instantiate the object and send messages manually within an unstructured code blocks. According to PHP manual a class is defined like this: <?php class A { function foo() { if (isset($this)) { echo '$this is defined ('; echo get_class($this); echo ")\n"; } else { echo "\$this is not defined.\n"; } } } class B { function bar() { A::foo(); } } $a = new A(); $a->foo(); A::foo(); $b = new B(); $b->bar(); B::bar(); ?> The part after class definitions is an unstructured program block where we lose readability, reusability, mantainability and all the other benefits of oop. Most of cli apps we use are showing what are avaliable commands and subcommands recursively: $ composer ... Avaliable Commands: about - ... archive - ... browse - ... $ composer browse -h Arguments: packages - ... Inspired by this, an object we create should also orient us where to go after each command. I will explain this later. Let's have one class called B, similar to above example. class B{ // No way to define order of methods public function __construct($world = "world"){ echo "Hello ". $world."\n"; echo "You can foo and you can bar\n"; } public function foo($x, $y){ echo "\nYou did foo: x is ".$x." and y is ".$y."\n"; echo "Now you can bar\n"; } public function bar($z, $t){ echo "\nYou did bar: z is ".$z." and t is ".$t."\n"; echo "You can bar again or end the program\n"; } public function __destruct(){ echo "\nGood bye\n"; } } // Here comes the spaghetti $app = new B("midori"); $app->foo(1,2); $app->bar(3,4); Instead of instantiating objects on the fly, here I propose a structured definition of an object using this syntax: object $app instanceof B{ $world = "midori"; // allowed methods comes after method definition // in the beginning state allowed methods are foo and bar public function __construct($world){ // if method body is implemented, // parent class method run automatically // object method runs after. // This block runs $app = new B($world); $this->foo(1,2); }(foo, bar); // The only allowed method is bar in this state public function foo(1,2)(bar); // if allowed methods are empty or not defined // object is destructed public function bar(3,4){ }; } An object can be instance of a class, if it's not defined, is instance of an anonymous class.
-- Midori Koçak Computer Scientist & Engineer http://www.mynameismidori.com <http://www.mtkocak.com/> “*The most beautiful thing we can experience is the mysterious. It is the source of all true art and science.*” Albert Einstein

Fleshgrinder

10 years ago
On 7/31/2016 11:00 AM, Midori Koçak wrote:
> https://wiki.php.net/rfc/structured_object_notation >
-1 The RFC is badly written and hard to understand right away. I would usually offer my help at this point but I think that the proposed feature is useless. You should also consider investing some time looking into other languages and document who else has this kind of feature you are proposing and document that in the RFC.
-- Richard "Fleshgrinder" Fussenegger

Dan Ackroyd

10 years ago
Hi FG, On 31 July 2016 at 14:15, Fleshgrinder <php@fleshgrinder.com> wrote: Unless you're deliberately trying to be offensive, please consider phrasing your words to not appear to be quite so rude. For example you could have said: "Sorry, I've read the RFC mutltiple times and can't understand what the purpose of it is. Is this feature similar to anything in other languages, that you can point us to?" Instead your words just insult someone who is trying to contribute. cheers Dan

Fleshgrinder

10 years ago
On 7/31/2016 4:30 PM, Dan Ackroyd wrote:
> Hi FG, > > On 31 July 2016 at 14:15, Fleshgrinder <php@fleshgrinder.com> wrote: > > Unless you're deliberately trying to be offensive, please consider > phrasing your words to not appear to be quite so rude. > > For example you could have said: > > "Sorry, I've read the RFC mutltiple times and can't understand what > the purpose of it is. Is this feature similar to anything in other > languages, that you can point us to?" > > Instead your words just insult someone who is trying to contribute. > > cheers > Dan >
I am sorry, I am definitely not trying to be offensive nor to insult anyone. I guess I just wrote what I thought without considering the possible impact of the phrasing.
-- Richard "Fleshgrinder" Fussenegger

Rowan Collins

10 years ago
On 31/07/2016 10:00, Midori Koçak wrote:
> https://wiki.php.net/rfc/structured_object_notation > > Object Oriented Programming is a key feature to write structured programs > in PHP. However, even though we have classes included in our program, the > only way to create an object from a class, is to instantiate it in > unstructured code.
Hi Midori, I'm not really sure what problem you're trying to solve here, or what exactly you're proposing. The RFC would benefit greatly from a succinct summary of the actual feature, as well as the examples. Are you looking for a way to set pre-requisites, like "you can only call bar() if you've already called foo()"? But then why are these on each instance, not part of the class? Or are you trying to declare the order of all operations in your program as some kind of directed graph? How would that work with multiple objects interacting, or with structures like conditionals and loops? There may be an interesting concept buried here somewhere, but I would echo Richard's suggestion of finding something similar in another language, or perhaps some related computer science theory, to give more depth to the idea. Regards,
-- Rowan Collins [IMSoP]

Dan Ackroyd

10 years ago
Hi Midori, On 31 July 2016 at 10:00, Midori Koçak <mtkocak@gmail.com> wrote:
> even though we have classes included in our program, the > only way to create an object from a class, is to instantiate it in > unstructured code.
That is the true nature of reality. All PHP programs start in procedural code, and then require some bootstrapping before we can enter the lovely world of OO code.
> Most of the programs we write, end up to have some piece of > spaghetti code somewhere. To prevent this, one should write a god object
and run once but classes are not and should not be code blocks wrapped around “class” keyword. All applications start in 'procedural' land. And all applications require some procedural code to setup the OO environment. You don't need to create a god object. What you need to do is be able to bootstrap the OO environment nicely from procedural code. There is no possible way to escape this. If you try to, what you find is that you're hiding the 'yucky' procedural stuff away somewhere. It will still be present, just not in as obvious a location.
> Instead of instantiating objects on the fly, here I propose a structured > definition of an object using this syntax:
I think what you've done is moved the procedural code into the object instantiation block. It is still there as procedural code, it just isn't as obvious. So it doesn't really solve the "problem" of PHP programs starting of in procedural code, it just makes it harder to see, which bits are procedural and which bits are OO.
> Consider this piece of code:
> $app->get('/hello/{name}', function (Request $request, Response $response) { > $name = $request->getAttribute('name'); > $response->getBody()->write("Hello, $name"); > > return $response; > });
I think quite a few frameworks show examples that look cool, but aren't necessarily the best practice. I've been using the Auryn DIC (https://github.com/rdlowrey/auryn) to do this bootstrapping required before we can use OO code. For the example code you showed, I would refactor it to use Auryn with something like this: // In an appropriate functions file function helloName(Request $request, Response $response) { $name = $request->getAttribute('name'); $response->getBody()->write("Hello, $name"); return $response; } //This is a procedural function that returns an object. i.e. it forms the boundary between the OO code in our application, and the procedural code. function createRoutes() { // We could read the routes from a data file, or even from annotations // Both of those would be just as procedural as this is. $getRoutes = [ '/hello/{name}' => 'helloName' ]; return new Routes($getRoutes) } class Application extends App { public function __construct(Routes $routes) { foreach ($routes->listGetRoutes() as $pattern => $callable) { $this->get($pattern, $callable); } } public function dispatch(Request $request) { // Do the dispatching of the request against the routes. } } // In bootstrap.php $injector->delegate('Routes', 'createRoutes'); // Another function to create a request object, from the $_REQUEST and other global vars. $injector->delegate('Request', 'createRequest'); $app = $injector->execute(['Application', 'dispatch']); The 'delegate' method is the bit where we creating the link between the lovely OO code, and the 'yucky' procedural code. The delegate method tells the injector, "whenever an some code we're running requires an object of types 'Route' call this function to create it". When the injector creates the Application object, it will see that the constructor needs a Routes object, and will call the delegate function to create it. Although we still have some lines of procedural code, it is reduced to the bare minimum, and none of the procedural code in the 'createRoutes' function needs to be cluttering up the bootstrap file. cheers Dan

Rowan Collins

10 years ago
On 31/07/2016 15:37, Dan Ackroyd wrote:
> All PHP programs start in procedural code, and then require some > bootstrapping before we can enter the lovely world of OO code.
I think it goes deeper than that: the procedural code is not just outside the objects, it's inside them too. The body of every method is a procedural list of statements and control structures. If you have a single "if" or "foreach" in your code, that's procedural code; if you have a method with more than one statement, that's procedural code. It's possible to have a "pure OO" language - take a look at Self or Smalltalk, where even "if" is a method call; or there's functional programming, where you try to "compose" actions, rather than declaring a sequence of them. But PHP, like Java, Ruby, Python, etc, etc, is at heart a language where you say "do this, then do that"; changing that isn't a minor enhancement to the language, it's a completely different approach to program structure. If the problem Midori is trying to solve is to do with bootstrapping, or dependency management, then I agree that a framework of some sort is the way to go. The boring glue code is all still there, but you don't need to look at it; ultimately, that's all a language feature could do anyway. Regards,
-- Rowan Collins [IMSoP]

Stas Malyshev

10 years ago
Hi!
> <?php > use \Psr\Http\Message\ServerRequestInterface as Request; > use \Psr\Http\Message\ResponseInterface as Response; > > require '../vendor/autoload.php'; > > $app = new \Slim\App; > $app->get('/hello/{name}', function (Request $request, Response $response) { > $name = $request->getAttribute('name'); > $response->getBody()->write("Hello, $name"); > > return $response; > }); > $app->run();
> This causes loss of readability and benefits of oop in most of the > programs. Most of the programs we write, end up to have some piece of
I don't see any loss of readability. Which part is not readable?
> // Here comes the spaghetti > $app = new B("midori"); > $app->foo(1,2); > $app->bar(3,4);
Don't see any spaghetti here. Are you using this term in some other meaning that is commonly used?
> Instead of instantiating objects on the fly, here I propose a structured > definition of an object using this syntax: > > object $app instanceof B{ > $world = "midori"; > // allowed methods comes after method definition > // in the beginning state allowed methods are foo and bar > public function __construct($world){ > // if method body is implemented, > // parent class method run automatically > // object method runs after. > // This block runs $app = new B($world); > $this->foo(1,2); > }(foo, bar); > > // The only allowed method is bar in this state > public function foo(1,2)(bar); > > // if allowed methods are empty or not defined > // object is destructed > public function bar(3,4){ > }; > }
Here we've got loss of readability. What is "}(foo, bar);" ? I have no idea. Let me parse it. foo and bar look in this context like constants, but I don't see any definitions. Maybe defined elsewhere? Fine. (foo, bar) looks like function invocation, taking these constants as parameters. But what is the function? Obviously something that precedes the (). So you are calling constructor with two arguments foo and bar? But constructor only accepts one argument! I'm lost now. Next:
> public function foo(1,2)(bar);
This looks like double function call - but why it happens in a definition of function? What is function foo?
> public function bar(3,4){ > };
OK, this looks like function definition - curly braces and stuff. But why the arguments are 3,4? Confused again. Sorry, I think this is overly complicated way of doing something that we can already do in easy way that 100% of the users can understand.
-- Stas Malyshev smalyshev@gmail.com