Hi Internals,
This email is to announce the formal opening of discussion for an RFC
to clean up the behaviour of the constructors shown by several
internal classes.
https://wiki.php.net/rfc/internal_constructor_behaviour
For reference this was discussed before
https://marc.info/?l=php-internals&m=142150339323854&w=2
As this RFC targets PHP 7, I plan to open the voting before the cut-off date.
cheers
Dan
Hi Internals,
This email is to announce the formal opening of discussion for an RFC
to clean up the behaviour of the constructors shown by several
internal classes.https://wiki.php.net/rfc/internal_constructor_behaviour
For reference this was discussed before
https://marc.info/?l=php-internals&m=142150339323854&w=2
Thank you for proceeding with this RFC.
Hi Dan,
Am 01.03.2015 um 15:55 schrieb Dan Ackroyd:
Hi Internals,
This email is to announce the formal opening of discussion for an RFC
to clean up the behaviour of the constructors shown by several
internal classes.https://wiki.php.net/rfc/internal_constructor_behaviour
I really like this RFC as it reduces WTF moments a lot!
One note from my perspective:
The class "PDORow" is not instantiable and triggers a fatal errors
currently. You propose to change the fatal error into an exception.
That's definitely better but it's an behavior not possible to
implemented in user land code as it would be impossible to get such an
object.
To define a class not instantiable in user land would be to define the
constructor private/protected.
Btw. What are the reasons to not make this class instantiable / extendable ?
Thoughts?
For reference this was discussed before
https://marc.info/?l=php-internals&m=142150339323854&w=2As this RFC targets PHP 7, I plan to open the voting before the cut-off date.
cheers
Dan
What are the reasons to not make this class instantiable / extendable ?
That class handles database state / resources that are not exposed
through the userland classes. It's correct in my opinion for it to not
be extendible or instantiable. Obviously it would be awesome if they
were covered by an interface, to allow testign to be easier, but
that's a separate issue.
That's definitely better but it's an behavior not possible to implemented in
user land code as it would be impossible to get such an object.
The same behaviour can seen with the code below.
cheers
Dan
class Foo {
private static $internalConstruction = false;
public function __construct() {
$constructionAllowed = !self::$internalConstruction;
self::$internalConstruction = false;
if ($constructionAllowed == false) {
throw new \Exception("External construction not allowed");
}
}
public static function create() {
self::$internalConstruction = true;
return new self();
}
}
$foo = Foo::create();
var_dump($foo);
$bar = new Foo();
Am 01.03.2015 um 20:04 schrieb Dan Ackroyd:
What are the reasons to not make this class instantiable / extendable ?
That class handles database state / resources that are not exposed
through the userland classes. It's correct in my opinion for it to not
be extendible or instantiable. Obviously it would be awesome if they
were covered by an interface, to allow testign to be easier, but
that's a separate issue.That's definitely better but it's an behavior not possible to implemented in
user land code as it would be impossible to get such an object.
The same behaviour can seen with the code below.cheers
Danclass Foo {
private static $internalConstruction = false; public function __construct() { $constructionAllowed = !self::$internalConstruction; self::$internalConstruction = false; if ($constructionAllowed == false) { throw new \Exception("External construction not allowed"); } } public static function create() { self::$internalConstruction = true; return new self(); }
}
$foo = Foo::create();
var_dump($foo);
$bar = new Foo();
OK you are right but it's ugly code and the not callable constructor
needs to be documented as it's not visible without calling.
The following would be the standard way to go:
final class PDORow {
private function __construct() {
// ...
}
public static function create() {
return new self();
}
}
Marc
Hi!
This email is to announce the formal opening of discussion for an RFC
to clean up the behaviour of the constructors shown by several
internal classes.
I'm not sure why this RFC assumes the exception is much better than
returning null. Exceptions are harder to handle (requires separate
syntax construct) and they are more expensive at runtime (must create an
object, collect the backtrace, etc.). Also, not catching the exception
immediately usually will lead to a complete application failure (usually
with an obscure error message like "internal operation failed", since
displaying raw exceptions to the user can be unsafe) instead of
finer-grained handling.
It is a long standing tradition in PHP that operations that can be
expected to fail - like fopen()
, date_create()
, etc. - do not produce
fatal errors but instead return error values which can be checked for
and handled. It is true that some SPL classes departed from that, but
this IMO was not an improvement but rather an annoyance that each time
you use an SPL thing you have to write it in a try/catch block. That
leads to much more boilerplate code than necessary.
Now I understand that PHP model of doing things may be unusual for some
people that come from other language background where everything throws
exceptions for any slightest reason and there's no other means of error
checking. But I think we can do better rather than that and have more
flexible error handling - even if it would require people to be
introduced to a concept they were not familiar with before coming to
PHP, such as not using exceptions for everything.
--
Stas Malyshev
smalyshev@gmail.com
Hi Stas,
On Mon, Mar 2, 2015 at 7:05 AM, Stanislav Malyshev smalyshev@gmail.com
wrote:
This email is to announce the formal opening of discussion for an RFC
to clean up the behaviour of the constructors shown by several
internal classes.I'm not sure why this RFC assumes the exception is much better than
returning null. Exceptions are harder to handle (requires separate
syntax construct) and they are more expensive at runtime (must create an
object, collect the backtrace, etc.). Also, not catching the exception
immediately usually will lead to a complete application failure (usually
with an obscure error message like "internal operation failed", since
displaying raw exceptions to the user can be unsafe) instead of
finer-grained handling.
It is a long standing tradition in PHP that operations that can be
expected to fail - likefopen()
,date_create()
, etc. - do not produce
fatal errors but instead return error values which can be checked for
and handled. It is true that some SPL classes departed from that, but
this IMO was not an improvement but rather an annoyance that each time
you use an SPL thing you have to write it in a try/catch block. That
leads to much more boilerplate code than necessary.Now I understand that PHP model of doing things may be unusual for some
people that come from other language background where everything throws
exceptions for any slightest reason and there's no other means of error
checking. But I think we can do better rather than that and have more
flexible error handling - even if it would require people to be
introduced to a concept they were not familiar with before coming to
PHP, such as not using exceptions for everything.
I can understand your argument. It works.
This proposal is about code modularization. Software engineering is
a history of better modularization.
- Structured programming
- Object Oriented programming
- AOP
- DbC
Exception is one of them.
While error handling without exception works, it decreases modularization.
Therefore, it decreases maintainability and increases chance of bugs.
Besides modularization, consistency is important for programming language
also.
These are the topic/objective for this RFC. IMHO.
Regards,
--
Yasuo Ohgaki
yohgaki@ohgaki.net
Hi!
This proposal is about code modularization. Software engineering is
a history of better modularization.
- Structured programming
- Object Oriented programming
- AOP
- DbC
Exception is one of them.
I'm sorry, I have hard time understanding what exceptions have to do
with modularization. Modularization is in my book creating a stand-alone
reusable software components that are responsible for distinct task or
set of tasks and have a well-defined API for that. I don't see where
exceptions fit here.
While error handling without exception works, it decreases modularization.
I don't see how. In what aspects and by what modularization is decreased?
Besides modularization, consistency is important for programming
language also.
Consistency is important, true. But I don't see why there's less
consistency in producing null values. On the contrary, the situation
where the same operation called via factory method can produce null
result, but literally exactly the same code called in different way via
"new" operator can not and would produce exception instead - doesn't
seem very consistent to me.
--
Stas Malyshev
smalyshev@gmail.com
I'm not sure why this RFC assumes the exception is much better than
returning null.It is a long standing tradition in PHP that operations that can be
expected to fail - likefopen()
.. do not produce
fatal errors but instead return error values which can be checked for
and handled.
The difference is that fopen can fail even if passed parameters that
are correct e.g. due to file permission problems. Because of that the
failure is not exceptional and so needs to be checked. Having to do
this through exceptions would be annoying which is why people prefer
to use procedural function for handling files, even when OO style
interfaces are available.
When a user calls new NumberFormatter()
they can know ahead of time
whether the parameters are valid or not. Any call with valid
parameters will succeed. Calls with invalid parameters will fail.
Because the programmer should only be passing in valid paramters any
failure is by it's nature an unexpected error.
And to be precise, fopen does not return error values. It returns a
single error value of false. There is no indication of why the error
occurred. Exceptions allow a message to be attached saying what went
wrong, which is why they are preferred in a lot of cases.
But I think we can do better rather than that and have more
flexible error handling - even if it would require people to be
introduced to a concept they were not familiar with before coming to
PHP, such as not using exceptions for everything.
You are one of the very few people who don't like exceptions. I
completely agree exceptions shouldn't be used everywhere and that they
should be used only for exception circumstances. But the vast majority
of PHP programmers are completely used to them.
If you don't see the benefits of using exceptions in some cases by
now, it's very unlikely that any argument is going to persuade you of
how they can be used (judiciously) to write clean code.
Luckily, this RFC isn't a general discussion about error handling, it
is specifically about the behaviour of internal classes being
'special' either by having behaviour which is not possible for
userland classes to do, or just being unusable after being
instantiated due to incorrect parameters.
cheers
Dan
Hi!
The difference is that fopen can fail even if passed parameters that
are correct e.g. due to file permission problems. Because of that the
failure is not exceptional and so needs to be checked. Having to do
Failure of the formatter on wrong format or data is of the same kind -
its not exceptional and only caused by input data that could be right
but in this circumstance aren't.
When a user calls
new NumberFormatter()
they can know ahead of time
whether the parameters are valid or not. Any call with valid
I certainly don't see how this can be true. The code can receive
generated format and user-supplied locale (e.g. loaded from resource
files and HTTP headers) and the developer can have no idea if it's
correct or not. Exactly the same as with files or date_create()
.
Because the programmer should only be passing in valid paramters any
failure is by it's nature an unexpected error.
That's exactly like saying programmer should only try to open existing
files so any failure by its nature is an unexpected error. You are
trying to create a difference when there's none. But if you don't like
fopen()
there are many other factory functions that behave the same -
like date_create()
or simplexml_load_string()
. You can claim that all
these failures are somehow "not exceptional" and only date formatting
using NumberFormatter is somehow "exceptional" but that doesn't sound
very logical to me.
And to be precise, fopen does not return error values. It returns a
single error value of false. There is no indication of why the error
occurred. Exceptions allow a message to be attached saying what went
wrong, which is why they are preferred in a lot of cases.
This is not relevant since error information can be retrieved by other
methods, and intl has such facility (as do other APIs, most of the APIs
have facilities to retrieve error information and very rarely they rely
on exceptions for that - exceptions are not very good means of doing that).
You are one of the very few people who don't like exceptions. I
Who said I don't like exceptions? I like exceptions, I adore, cherish
and admire them - when they are used appropriately. What I am saying is
not that exceptions are universally bad - but they are also not
universally good, they should be used sparingly, in appropriate places,
and not every situation where something is not going as planned should
necessarily be an exception.
should be used only for exception circumstances. But the vast majority
of PHP programmers are completely used to them.
They can not be used to them in the context of this RFC since PHP never
had them in such context. They can be used to them in different
contexts, but I've also seen a lot of boilerplate code and unclear error
reporting growing from the fact that some code is too exception-happy.
Luckily, this RFC isn't a general discussion about error handling, it
is specifically about the behaviour of internal classes being
'special' either by having behaviour which is not possible for
userland classes to do, or just being unusable after being
instantiated due to incorrect parameters.
If something is unusable after being instantiated, it would better just
let the user handle it instead of producing bad object. I agree with
that - if you produce an object, it should be useful, otherwise no point
in it. However, I disagree with the other part.
--
Stas Malyshev
smalyshev@gmail.com
Because the programmer should only be passing in valid paramters any
failure is by it's nature an unexpected error.
That's exactly like saying programmer should only try to open existing
files so any failure by its nature is an unexpected error. You are
trying to create a difference when there's none. But if you don't like
fopen()
there are many other factory functions that behave the same -
likedate_create()
orsimplexml_load_string()
. You can claim that all
these failures are somehow "not exceptional" and only date formatting
using NumberFormatter is somehow "exceptional" but that doesn't sound
very logical to me.
If an object can not be created then the handle should be null. That is
all that is needed. It's no different to a result being an empty string.
If one is catering for the fact that there is no result then it's not an
exception and we either handle the empty result or just carry on.
Nothing here has any bearing on 'modular programming' it is all
perfectly well handled.
And to be precise, fopen does not return error values. It returns a
single error value of false. There is no indication of why the error
occurred. Exceptions allow a message to be attached saying what went
wrong, which is why they are preferred in a lot of cases.
This is not relevant since error information can be retrieved by other
methods, and intl has such facility (as do other APIs, most of the APIs
have facilities to retrieve error information and very rarely they rely
on exceptions for that - exceptions are not very good means of doing that).
Simply follows on from above. If we need to know why there was no object
created we ask ... if we have no need to know AT THIS STAGE we would
have to handle the exception to get back to the normal flow?
You are one of the very few people who don't like exceptions. I
Who said I don't like exceptions? I like exceptions, I adore, cherish
and admire them - when they are used appropriately. What I am saying is
not that exceptions are universally bad - but they are also not
universally good, they should be used sparingly, in appropriate places,
and not every situation where something is not going as planned should
necessarily be an exception.
Exceptions are perfectly valid when the work flow has to be interrupted
for something that can't be catered for in the normal flow. 'Engine'
exceptions due to lack of resources and trying to execute something when
the object was not created. There are certainly 'exceptions in the
engine' where catch would be helpful and perhaps avoid the number of
white screens I currently see, but I feel that THAT particular RFC may
open the door for more general 'exception' switches?
should be used only for exception circumstances. But the vast majority
of PHP programmers are completely used to them.
They can not be used to them in the context of this RFC since PHP never
had them in such context. They can be used to them in different
contexts, but I've also seen a lot of boilerplate code and unclear error
reporting growing from the fact that some code is too exception-happy.
Seconded ... that people try to work in ways they are used to rather
than in ways that are operationally more efficient is the problem. Just
as many 'older' PHP users are more used to a different style of
'modularity'. It's not wrong and it may be that better education is what
is missing ... both ways.
Luckily, this RFC isn't a general discussion about error handling, it
is specifically about the behaviour of internal classes being
'special' either by having behaviour which is not possible for
userland classes to do, or just being unusable after being
instantiated due to incorrect parameters.
If something is unusable after being instantiated, it would better just
let the user handle it instead of producing bad object. I agree with
that - if you produce an object, it should be useful, otherwise no point
in it. However, I disagree with the other part.
We can agree that any process that can create a handle to an object
which can't actually function as requested then it is an error that
needs fixing. Some of the shortcoming of PDO may well be candidates for
an exception, but that is only because they don't follow the rules used
for other database extensions. Similarly a number of the other packages
you have identified need similar shortcoming fixed, but FIXED rather
than simply now adding an exception to hide the poor modularity of the
package!
The 'correct' work flow question should be "Can I correct the error in
the parameters passed?" If the answer to this is yes, then the object
should at least be in a stable state to allow that. If there is no
object, perhaps because a file does not exist, then the work flow may
already define the next step ... create the file ... then if that does
not work one may bow out with a null object, or leave an object with a
valid handle but with a state showing why it's an empty object.
The only package I use on the list is PDO, and where that returns 'null'
is totally the correct result. I can see where this is also perfectly
correct for some of the other situations as well. The Reflection stuff
is something I have yet to find an use for ... a decent IDE is still the
right place to do most of that ... but your examples are areas where the
other RFC comes into play anyway.
--
Lester Caine - G8HFL
Contact - http://lsces.co.uk/wiki/?page=contact
L.S.Caine Electronic Services - http://lsces.co.uk
EnquirySolve - http://enquirysolve.com/
Model Engineers Digital Workshop - http://medw.co.uk
Rainbow Digital Media - http://rainbowdigitalmedia.co.uk
Hi!
This email is to announce the formal opening of discussion for an RFC
to clean up the behaviour of the constructors shown by several
internal classes.I'm not sure why this RFC assumes the exception is much better than
returning null. Exceptions are harder to handle (requires separate
syntax construct) and they are more expensive at runtime (must create an
object, collect the backtrace, etc.). Also, not catching the exception
immediately usually will lead to a complete application failure (usually
with an obscure error message like "internal operation failed", since
displaying raw exceptions to the user can be unsafe) instead of
finer-grained handling.
It is a long standing tradition in PHP that operations that can be
expected to fail - likefopen()
,date_create()
, etc. - do not produce
fatal errors but instead return error values which can be checked for
and handled. It is true that some SPL classes departed from that, but
this IMO was not an improvement but rather an annoyance that each time
you use an SPL thing you have to write it in a try/catch block. That
leads to much more boilerplate code than necessary.Now I understand that PHP model of doing things may be unusual for some
people that come from other language background where everything throws
exceptions for any slightest reason and there's no other means of error
checking. But I think we can do better rather than that and have more
flexible error handling - even if it would require people to be
introduced to a concept they were not familiar with before coming to
PHP, such as not using exceptions for everything.
I'm not sure I can understand your crusade against this topic.
Consistency with userland is beneficial, because the majority of PHP
developers probably do not expect new
to yield anything than a
concrete instance or an exception.
Quoting php.net/manual:
To create an instance of a class, the new keyword must be used. An
object will always be created unless the object has a constructor
defined that throws an exception on error.
Emphasis mine.
Do you know of any other case where a new
operator in an object
oriented context can return NULL, except C++ where you have to
explicitely ask for that behavior?
--
Regards,
Mike
I'm not sure I can understand your crusade against this topic.
Consistency with userland is beneficial, because the majority of PHP
developers probably do not expectnew
to yield anything than a
concrete instance or an exception.Quoting php.net/manual:
To create an instance of a class, the new keyword must be used. An
object will always be created unless the object has a constructor
defined that throws an exception on error.Emphasis mine.
And did you actually read them?
This is something that came in with 'constructors' and only when an
exception is required is one raised.
It is only that particular 'improvement' to coding style that brought in
the need for an exception, but something which seems to have been missed
is what should happen if the object is created but in a state that it
can't actually be used?
There are many cases where a null handle is still a more practical
return and that element of PHP seems to have been lost? That the manual
does not reflect the whole of the language is a well established fact :(
Do you know of any other case where a
new
operator in an object
oriented context can return NULL, except C++ where you have to
explicitely ask for that behavior?
One might ask why C++ programmers see an advantage in that particular
action I have my own style of working which originated in Algol and
progressed to C++ prior to PHP so for me there is nothing wrong except
perhaps the increasing pressure to create objects where a simple data
value is all that is needed. PDORow is a holder for a set of data ... it
only needs to be called when one has a result set to view and once all
the results are viewed 'null' is it's natural state. Calling it without
a result set is an 'invalid number of arguments' but a resulting null
handle is still the correct state for the work flow and this is the case
in many of the empty objects which PHP naturally encounters? This is
just the same debate as 'empty string = false'!
--
Lester Caine - G8HFL
Contact - http://lsces.co.uk/wiki/?page=contact
L.S.Caine Electronic Services - http://lsces.co.uk
EnquirySolve - http://enquirysolve.com/
Model Engineers Digital Workshop - http://medw.co.uk
Rainbow Digital Media - http://rainbowdigitalmedia.co.uk
Hi!
I'm not sure I can understand your crusade against this topic.
I would start with trying to address my actual arguments, as opposed to
dismissing it as a "crusade". I've described my arguments for it in my
previous emails, is there something unclear there? I'd be happy to
explain any point further.
Consistency with userland is beneficial, because the majority of PHP
developers probably do not expectnew
to yield anything than a
concrete instance or an exception.
Of course, consistency with userland is beneficial. However, in userland
we do not have many things that we have in internals - like operator
overloading, function aliasing, etc. - because some of the functionality
we consider risky and as such better to be left to internals/extensions.
This is one more example.
Quoting php.net/manual:
To create an instance of a class, the new keyword must be used. An
object will always be created unless the object has a constructor
defined that throws an exception on error.
Manual can be fixed.
Do you know of any other case where a
new
operator in an object
oriented context can return NULL, except C++ where you have to
explicitly ask for that behavior?
I can't say I am familiar with every OO language's implementation
details, so I don't know. But I don't see why, if we think it's useful,
we can not do it - especially given our factory methods/functions
already do it. I think it's useful.
Stas Malyshev
smalyshev@gmail.com
Consistency with userland is beneficial, because the majority of PHP
developers probably do not expectnew
to yield anything than a
concrete instance or an exception.Of course, consistency with userland is beneficial. However, in userland
we do not have many things that we have in internals - like operator
overloading, function aliasing, etc. - because some of the functionality
we consider risky and as such better to be left to internals/extensions.
This is one more example.
None of them cause a fatal error, because it behaves contrary to established
standards.
Quoting php.net/manual:
To create an instance of a class, the new keyword must be used. An
object will always be created unless the object has a constructor
defined that throws an exception on error.Manual can be fixed.
Everything can be “fixed”, but what’s the right thing to fix? Documentation
valid for a decade that covers 98% of use cases, which will cause everyone
to revisit any instance of new
with internal constructors or rather fix those
internal classes misbehaving.
Do you know of any other case where a
new
operator in an object
oriented context can return NULL, except C++ where you have to
explicitly ask for that behavior?I can't say I am familiar with every OO language's implementation
details, so I don't know. But I don't see why, if we think it's useful,
we can not do it - especially given our factory methods/functions
already do it. I think it's useful.
That’s okay, but do you think the majority of PHP developers finds that
undocumented and unexpected behaviour useful?
To me this is as useful as an internal class crashing because it doesn’t
check if it was instantiated correctly.
Regards,
Mike
Hi all,
Do you know of any other case where a
new
operator in an object
oriented context can return NULL, except C++ where you have to
explicitly ask for that behavior?I can't say I am familiar with every OO language's implementation
details, so I don't know. But I don't see why, if we think it's useful,
we can not do it - especially given our factory methods/functions
already do it. I think it's useful.That’s okay, but do you think the majority of PHP developers finds that
undocumented and unexpected behaviour useful?
To me this is as useful as an internal class crashing because it doesn’t
check if it was instantiated correctly.
Let's be consistent. If error happened during object instantiation, raise
exceptions.
Returning NULL
for this case would be OK. We may be better to be consistent
because exceptions can be ignored.
There are 2 possibilities, return NULL
or FALSE
Always return NULL
or FALSE
for errors?
Many procedural functions return FALSE
for error. Most notable exceptions
are argument count errors that return NULL. It may be better cleanup these
inconsistent behaviors also.
Regards,
--
Yasuo Ohgaki
yohgaki@ohgaki.net
Hi Stas,
On Mon, Mar 2, 2015 at 7:05 AM, Stanislav Malyshev smalyshev@gmail.com
wrote:
Hi!
This email is to announce the formal opening of discussion for an RFC
to clean up the behaviour of the constructors shown by several
internal classes.I'm not sure why this RFC assumes the exception is much better than
returning null. Exceptions are harder to handle (requires separate
syntax construct) and they are more expensive at runtime (must create an
object, collect the backtrace, etc.). Also, not catching the exception
immediately usually will lead to a complete application failure (usually
with an obscure error message like "internal operation failed", since
displaying raw exceptions to the user can be unsafe) instead of
finer-grained handling.
It is a long standing tradition in PHP that operations that can be
expected to fail - likefopen()
,date_create()
, etc. - do not produce
fatal errors but instead return error values which can be checked for
and handled. It is true that some SPL classes departed from that, but
this IMO was not an improvement but rather an annoyance that each time
you use an SPL thing you have to write it in a try/catch block. That
leads to much more boilerplate code than necessary.Now I understand that PHP model of doing things may be unusual for some
people that come from other language background where everything throws
exceptions for any slightest reason and there's no other means of error
checking. But I think we can do better rather than that and have more
flexible error handling - even if it would require people to be
introduced to a concept they were not familiar with before coming to
PHP, such as not using exceptions for everything.
I come up with better idea for both procedural and OO style error handling.
The other day, I wrote simple PostgreSQL and MongoDB benchmark
script to see how PostgreSQL outperforms MongoDB.
(PostgreSQL 9.4 is about 30% faster than MongoDB 2.6, BTW)
https://gist.github.com/yohgaki/a11a2f435f3d8f6ee096
For this kind of simple procedural code, exceptions are useless or even
harmful. If there is a RFC that ignores unhandled exceptions, no try block
and no default exception handler then ignore exception, I'll vote "yes" for
it.
Having this kind of switch would be great for procedural code and OO API
designers.
Just an idea.
Regards,
--
Yasuo Ohgaki
yohgaki@ohgaki.net