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
-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
Hi FG,
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
Hi FG,
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
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]
Hi Midori,
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
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]
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