On Fri, Sep 23, 2016 at 9:45 PM, Stanislav Malyshev <smalys...@gmail.com>
wrote:

> Hi!
>
> > The broader context of this proposal is to provide a simple and usable
> > mechanism that will allow developers to opt-in to stricter language
> > semantics on a per-library (or more specifically, per-namespace) basis,
> > thus alleviating backwards compatibility and library interoperability
> > concerns for such changes.
>
> I don't think it's a good idea. Not only we'd have two language
> semantics in one language - which is by itself very far from ideal - but
> you'd have absolutely no way of knowing which semantics is active for
> which file by just reading that file. You will have to consider all code
> that could potentially run up to this point, and all code paths that
> could have been taken, and could disable or enable strict context. It
> could also mean that the same code could actually run with both models,
> depending on the caller - which goes contrary to the whole point of
> strict declaration. It's way worse than ini setting - at least ini
> setting is supposed to be one for every install of the code and can't
> change in runtime depending on code paths.
>
> Moreover, this precludes any optimization decision from being made by
> opcode cache and such - if the same file code can be run in both strict
> and non-strict context, depending on what was executed before in the
> same request, it is impossible to make any optimization decision on
> per-file basis.
>

No, this is not how it would work. While I did not go into the
technicalities of the implementation in this proposal, this issue is
briefly mentioned:

> Namespace-scoped declares will have to be taken into account by opcache.
Namely, if a file is compiled with a certain set of namespace-scoped
declares, it cannot necessarily be reused if it is compiled with a
different set of declares. This could be solved by storing a checksum based
on the namespace-scoped declares at the time of compilation together with
the cached file and compare it when loading it. I believe this can be done
efficiently.

Compilation has to happen for a certain set of statically known declare
directives and the cached file will be fingerprinted to make sure it is not
reused if the declare directives are changed. In fact, it is not even
technically possible to treat certain declares (like ticks) at runtime,
because they require different codegen.

Moreover, this RFC clearly build an infrastructure for making more
> semantic forks, eventually leading to the situation where reader of the
> code has absolutely no idea, looking at the source of certain function,
> what is actually the semantics of the language and the rules it will be
> executed under. And neither, even worse, does the author of the code.
>
> If that were localized by file, it'd be bad but one could grudgingly
> tolerate it - you could scroll to the beginning of file and say "oh,
> sigh, now we're in PHP with strict types, but lax objects, but strict
> integers, but lax floats, but strict comparisons, but lax conditionals,
> but strict argument counts! Now I understand what's going on if I only
> keep in mind those 20 bits that are different in every file!". But after
> delocalizing it, all hope is lost - you never know what the code in the
> file actually means - because somebody could write code in completely
> different file, maybe even JSON composer configuration or some other
> config file you didn't even think to be able to change your language
> semantics - and suddenly all the code works differently.
>
>
> Or, for more fun, breaks differently. And you as code author have zero
> control over it because of the wonders of shared mutable state which now
> encompasses not only data but the very core of the language. Imagine how
> fun it is if somebody's action in different code module wouldn't just
> mess up some data - it would actually break your code by changing
> language semantics for your code!

Err, okay.

Say I am a Symfony user. Say that before loading the library I include a
file with the following content:

namespace Symfony\Whatever\Namespace;
function strlen($str) {
    return \strlen($str) + 1;
}
// Repeat for a few more namespaces.

This will end up hijacking uses of the strlen() function within the Symfony
codebase due to the way the global namespace fallback works.

OH MY GOD. If I can't even rely on the behavior of basic standard library
functions, what can I still rely on? A malicious user could completely
break my code! Nothing is certain anymore, all hope is lost! I should go
hide in the basement!

Of course, nobody is actually concerned about this. Yes, PHP is a
programming language, so you can break things pretty much however you like.
But it is common sense that you do not go about hijacking functions from
foreign namespaces and nobody is wasting time considering this possibility.

My analogy is probably a bit over the top, but I think this is really the
argument you're making. Yes, of course you can break things by setting
declares on foreign namespaces, but it wouldn't make any sense for anyone
to actually do this. Yes, you can cause confusion by choosing a different
set of declares for all the namespaces you use but ... why?

Realistically, if you maintain a library, you will have one global set of
declares you use for the entire library. I sure hope that it is not too
much to ask a library author to keep in mind the declares his project uses.

Actually, I would argue that it is much simpler to remember your global
library defaults, than to double-check whether the declares at the top of
the file you're currently editing are *really* the same as in the rest of
the project, or whether one option was maybe flipped. If you repeat all
your project defaults in every single file, you are bound to miss that one
case where dynamic_object_properties is set to 1 instead of 0, because that
particular file does require this functionality. If you have defaults that
are not repeated, this kind of explicit declare would stand out clearly.

Nikita

Reply via email to