Hello,
It looks like we miss a way to check by reflection if a function/method has
strict types enabled or not.
We'd need to do this in Symfony to generate a file with concatenated
classes, but split "declare_strict=1" classes out so that they don't break
(because there is no way to create a single file that contains both strict
and non-strict classes unfortunately also).
Would it be possible to expose some flavor of ZEND_ACC_STRICT_TYPES to
userland?
Regards,
Nicolas
2016-09-05 11:38 GMT+02:00 Nicolas Grekas nicolas.grekas@gmail.com:
Hello,
It looks like we miss a way to check by reflection if a function/method has
strict types enabled or not.
This isn't bound to functions / methods and can't be reflected.
We'd need to do this in Symfony to generate a file with concatenated
classes, but split "declare_strict=1" classes out so that they don't break
(because there is no way to create a single file that contains both strict
and non-strict classes unfortunately also).
declare_strict=1 affects only calls, not definitions. It matters in the
file that does the call, not the file where it is defined.
Would it be possible to expose some flavor of ZEND_ACC_STRICT_TYPES to
userland?Regards,
Nicolas
Hello,
It looks like we miss a way to check by reflection if a function/method has
strict types enabled or not.We'd need to do this in Symfony to generate a file with concatenated
classes, but split "declare_strict=1" classes out so that they don't break
(because there is no way to create a single file that contains both strict
and non-strict classes unfortunately also).Would it be possible to expose some flavor of ZEND_ACC_STRICT_TYPES to
userland?
Strict types are only enforced from the CALL site. A library author cannot dictate that their code be called using strict types or not, only the file actually calling the library methods can. Though, regardless, the type hints do ensure that the value received is of the type specified, you just won't know if it was coerced or not.
Just like when you import a namespaced class and alias it, the alias only works in the file the "use " statement is in.
Therefore, you cannot actually author strict or non-strict classes period, unless they are only called in the file in which they are defined.
This means you cannot reflect on it, unless we start doing reflection on source code files themselves.
- Davey
On Sep 5, 2016, at 02:38, Nicolas Grekas nicolas.grekas@gmail.com
wrote:Hello,
It looks like we miss a way to check by reflection if a function/method
has
strict types enabled or not.We'd need to do this in Symfony to generate a file with concatenated
classes, but split "declare_strict=1" classes out so that they don't
break
(because there is no way to create a single file that contains both
strict
and non-strict classes unfortunately also).Would it be possible to expose some flavor of ZEND_ACC_STRICT_TYPES to
userland?Strict types are only enforced from the CALL site. A library author cannot
dictate that their code be called using strict types or not, only the file
actually calling the library methods can. Though, regardless, the type
hints do ensure that the value received is of the type specified, you just
won't know if it was coerced or not.Just like when you import a namespaced class and alias it, the alias only
works in the file the "use " statement is in.Therefore, you cannot actually author strict or non-strict classes period,
unless they are only called in the file in which they are defined.
Actually you can, in PHP 5 even. This sort of thing is what the assert()
statement is for.
public function stringsOnly( $var ) {
assert(is_str($var), 'Strings Only!');
}
This does have the weakness of not working when assertions are turned off.
If $var is a form of user input then an exception test is called for, and
likely should be done before stringsOnly in this example gets worked with.
On Mon, Sep 5, 2016 at 11:38 AM, Nicolas Grekas
nicolas.grekas@gmail.com wrote:
Hello,
It looks like we miss a way to check by reflection if a function/method has
strict types enabled or not.We'd need to do this in Symfony to generate a file with concatenated
classes, but split "declare_strict=1" classes out so that they don't break
(because there is no way to create a single file that contains both strict
and non-strict classes unfortunately also).Would it be possible to expose some flavor of ZEND_ACC_STRICT_TYPES to
userland?
I guess what you miss for what you want to do, is to detect if strict
types are activated into the current scope, at runtime.
Here is a feature that solves that.
https://github.com/jpauli/php-src/tree/is_strict_types
Julien.P
Thanks for trying Julien
I guess what you miss for what you want to do, is to detect if strict
types are activated into the current scope, at runtime.
From the inside, the author of the code knows if they added the declare or
not.
I'd need to know from the outside, before concatenating it, if some file
has strict types.
This could be exposed on the reflection, since a function/method/class
could have a flag that tells if it has been compiled with strict types
enabled or not.
The current alternative is to parse the source to check if it starts with
the declare directive (but not trivial because of non semantic tokens).
Here is my current regex to do so:
$c = '(?:\s*+(?:(?:#|//)[^\n]+\n|/*(?:(?<!*/).)++)?+)+';
$strictTypesRegex = str_replace('.', $c, "'^<?php\s.declare.(.strict_
types.=.1.).;'is");
Nicolas
Thanks for trying Julien
I guess what you miss for what you want to do, is to detect if strict
types are activated into the current scope, at runtime.From the inside, the author of the code knows if they added the declare or
not.
I'd need to know from the outside, before concatenating it, if some file has
strict types.
This could be exposed on the reflection, since a function/method/class could
have a flag that tells if it has been compiled with strict types enabled or
not.
The current alternative is to parse the source to check if it starts with
the declare directive (but not trivial because of non semantic tokens).
Then you need reflection.
I just added ReflectionClass:isStrictlyTyped() and
ReflectionFunction::isStrictlyTyped() to my patch
https://github.com/jpauli/php-src/tree/is_strict_types
Julien.P
Thanks for trying Julien
I guess what you miss for what you want to do, is to detect if strict
types are activated into the current scope, at runtime.From the inside, the author of the code knows if they added the declare or
not.
I'd need to know from the outside, before concatenating it, if some file has
strict types.
This could be exposed on the reflection, since a function/method/class could
have a flag that tells if it has been compiled with strict types enabled or
not.
The current alternative is to parse the source to check if it starts with
the declare directive (but not trivial because of non semantic tokens).Then you need reflection.
I just added ReflectionClass:isStrictlyTyped() and
ReflectionFunction::isStrictlyTyped() to my patch
Any souls to +1 the reflection patch ?
Julien
2016-09-05 15:52 GMT+02:00 Julien Pauli jpauli@php.net:
On Mon, Sep 5, 2016 at 2:44 PM, Nicolas Grekas nicolas.grekas@gmail.com
wrote:Thanks for trying Julien
I guess what you miss for what you want to do, is to detect if strict
types are activated into the current scope, at runtime.From the inside, the author of the code knows if they added the declare
or
not.
I'd need to know from the outside, before concatenating it, if some
file has
strict types.
This could be exposed on the reflection, since a function/method/class
could
have a flag that tells if it has been compiled with strict types
enabled or
not.
The current alternative is to parse the source to check if it starts
with
the declare directive (but not trivial because of non semantic tokens).Then you need reflection.
I just added ReflectionClass:isStrictlyTyped() and
ReflectionFunction::isStrictlyTyped() to my patchhttps://github.com/jpauli/php-src/tree/is_strict
https://github.com/jpauli/php-src/tree/is_strict_types
Julien
--
**
_types https://github.com/jpauli/php-src/tree/is_strict_types
Any souls to +1 the reflection patch ?
ReflectionFunction::isStrictlyTyped doesn't make sense. As mentioned
already, this doesn't depend on the function declaration but on the CALLER
instead.
Regards, Niklas
Hi,
On Mon, Sep 5, 2016 at 2:44 PM, Nicolas Grekas nicolas.grekas@gmail.com
wrote:Thanks for trying Julien
I guess what you miss for what you want to do, is to detect if strict
types are activated into the current scope, at runtime.From the inside, the author of the code knows if they added the
declare or
not.
I'd need to know from the outside, before concatenating it, if some
file has
strict types.
This could be exposed on the reflection, since a function/method/class
could
have a flag that tells if it has been compiled with strict types
enabled or
not.
The current alternative is to parse the source to check if it starts
with
the declare directive (but not trivial because of non semantic tokens).Then you need reflection.
I just added ReflectionClass:isStrictlyTyped() and
ReflectionFunction::isStrictlyTyped() to my patchAny souls to +1 the reflection patch ?
I am not sure about it. Given the concept being strict type it should not
matter, at all (given the reasons well explained here and in the RFC). Or
do I miss something?
being strict type it should not matter, at all
It doesn't, except when trying to concatenate several source files into a
single bigger one.
Then we're stuck because declare(strict_type=0/1) is specifically not
allowed in the middle of a file, and not allowed to use the block syntax
(declare(strict_type=0/1) {...}).
In fact, the feat I was asking for could be seen as a workaround for this
limitation. Maybe it would be better to allow some syntax to be able to
create one big file that contains class declatation foo1 & foo2, where foo1
is strict and foo2 isn't?
Nicolas
2016-09-05 17:25 GMT+02:00 Nicolas Grekas nicolas.grekas@gmail.com:
being strict type it should not matter, at all
It doesn't, except when trying to concatenate several source files into a
single bigger one.
Then we're stuck because declare(strict_type=0/1) is specifically not
allowed in the middle of a file, and not allowed to use the block syntax
(declare(strict_type=0/1) {...}).In fact, the feat I was asking for could be seen as a workaround for this
limitation. Maybe it would be better to allow some syntax to be able to
create one big file that contains class declatation foo1 & foo2, where foo1
is strict and foo2 isn't?Nicolas
Why do you concatenate these files in the first place?
Why do you concatenate these files in the first place?
It's not specifically me but Symfony's ClassCollectionLoader::load()
method, which generates a single file with common classes inlined for
faster bootstrapping (the perf gain is objectively measurable).
You give this method a list of classes that are always required, and it
computes all the required parents/interfaces/traits they use, by using
reflection of course. It turns namespace to the bracketed syntax, orderd
things as required (parents first), and dumps everything in one php file.
The issue came to our tracker that this process was broken once one wanted
to inline a strict class there, either directly or indirectly through the
parent chain.
I worked on the issue and fixed it by replacing the inlining by a "require"
for now.
Here is the pointer for the fix (then the class & the issue):
https://github.com/symfony/symfony/pull/19859/files?w=1
On Mon, Sep 5, 2016 at 1:13 PM, Nicolas Grekas nicolas.grekas@gmail.com
wrote:
Why do you concatenate these files in the first place?
It's not specifically me but Symfony's ClassCollectionLoader::load()
method, which generates a single file with common classes inlined for
faster bootstrapping (the perf gain is objectively measurable).You give this method a list of classes that are always required, and it
computes all the required parents/interfaces/traits they use, by using
reflection of course. It turns namespace to the bracketed syntax, orderd
things as required (parents first), and dumps everything in one php file.The issue came to our tracker that this process was broken once one wanted
to inline a strict class there, either directly or indirectly through the
parent chain.
Again, there is not such thing as a strict class. You should be stripping
ALL declare(strict_types=0/1) from the class files, and even if enabled it
at the top of final generated file it wouldn't make any difference unless
you're calling the classes IN the generated file. Anything you include it
into will decide for itself whether it is strict or not.
I worked on the issue and fixed it by replacing the inlining by a "require"
for now.Here is the pointer for the fix (then the class & the issue):
https://github.com/symfony/symfony/pull/19859/files?w=1
I would highly recommend using sprintf()
instead of str_replace()
to inject
your regex. And again, strip the declare lines completely. Then you can
concat.
https://github.com/symfony/symfony/pull/19859/files?w=1#diff-e37b3a45b919f2dd526570353388e4a5R1
This isn't a class with strict types, it's a file that will enforce strict
types on code that it calls, not code that calls the class defined inside
it.
- Davey
On Mon, Sep 5, 2016 at 1:13 PM, Nicolas Grekas nicolas.grekas@gmail.com
wrote:Why do you concatenate these files in the first place?
It's not specifically me but Symfony's ClassCollectionLoader::load()
method, which generates a single file with common classes inlined for
faster bootstrapping (the perf gain is objectively measurable).You give this method a list of classes that are always required, and it
computes all the required parents/interfaces/traits they use, by using
reflection of course. It turns namespace to the bracketed syntax, orderd
things as required (parents first), and dumps everything in one php file.The issue came to our tracker that this process was broken once one
wanted
to inline a strict class there, either directly or indirectly through the
parent chain.Again, there is not such thing as a strict class. You should be stripping
ALL declare(strict_types=0/1) from the class files, and even if enabled it
at the top of final generated file it wouldn't make any difference unless
you're calling the classes IN the generated file. Anything you include it
into will decide for itself whether it is strict or not.
Stripping strict_types declaration will change the behavior of the code
declared within those files (not the code calling that code). The
behavior should not change in the merge process.
As I see it, the issue here is really not how to figure out whether a file
uses strict_types, the issue is how you mix strict_types=1 and
strict_types=0 code in a single file.
Back when this feature was introduced, we decided not to allow this kind of
mixing, as it seemed prone to causing a mess and use-cases seemed doubtful.
Given the issue Symfony is experiencing, we should reevaluate this
decision. Imho it's not good if there are some things you simply cannot
express in a single file.
Nikita
Stripping strict_types declaration will change the behavior of the code
declared within those files (not the code calling that code). The
behavior should not change in the merge process.As I see it, the issue here is really not how to figure out whether a file
uses strict_types, the issue is how you mix strict_types=1 and
strict_types=0 code in a single file.Back when this feature was introduced, we decided not to allow this kind of
mixing, as it seemed prone to causing a mess and use-cases seemed doubtful.
Given the issue Symfony is experiencing, we should reevaluate this
decision. Imho it's not good if there are some things you simply cannot
express in a single file.Nikita
Well, I still think that the declare approach is kind of weird and
creates two PHP worlds and that it would be easier if scalar type hints
are always enforced. However, I do not see why it is an issue for
Symfony here.
One can use the tokenizer to check if a file has a strict declare in it
and instead of creating a single output file create two. One that has no
strict declare and one that has a strict declare.
It might have a slight performance impact compared to a single file but
it solves the issue at hand.
The issue you describe (inability to express something in a single file)
is part of the design of declare ...
--
Richard "Fleshgrinder" Fussenegger
Hi Nikita,
Nikita Popov wrote:
As I see it, the issue here is really not how to figure out whether a file
uses strict_types, the issue is how you mix strict_types=1 and
strict_types=0 code in a single file.
This is also my assessment.
Back when this feature was introduced, we decided not to allow this kind of
mixing, as it seemed prone to causing a mess and use-cases seemed doubtful.
Given the issue Symfony is experiencing, we should reevaluate this
decision. Imho it's not good if there are some things you simply cannot
express in a single file.
I think it was things like this that made me initially allow having
multiple strict_types declarations in a file. But that was changed later.
Maybe we could allow it, but only with the block syntax, much as we do
with namespaces? i.e.:
declare (strict_types=1) {
}
declare (strict_types=0) {
}
Or we could allow any number of non-block strict_types declarations if
we wanted. I don't know.
--
Andrea Faulds
https://ajf.me/
Hi Nikita,
Nikita Popov wrote:
As I see it, the issue here is really not how to figure out whether a file
uses strict_types, the issue is how you mix strict_types=1 and
strict_types=0 code in a single file.This is also my assessment.
Back when this feature was introduced, we decided not to allow this kind of
mixing, as it seemed prone to causing a mess and use-cases seemed
doubtful.
Given the issue Symfony is experiencing, we should reevaluate this
decision. Imho it's not good if there are some things you simply cannot
express in a single file.I think it was things like this that made me initially allow having
multiple strict_types declarations in a file. But that was changed later.Maybe we could allow it, but only with the block syntax, much as we do
with namespaces? i.e.:declare (strict_types=1) {
}
declare (strict_types=0) {
}
Or we could allow any number of non-block strict_types declarations if we
wanted. I don't know.
Next to the question of which syntax specifically we want to allow, this
will also be somewhat challenging to implement, or may require further
semantic restrictions. Currently we only store strict_type information at
the op_array level, which means that we have no way of representing
something like:
declare(strict_types=1) {
foo($bar);
}
declare(strict_types=0) {
foo($bar);
}
This means that we either
a) have to represent strict_types at a more granular level, which
effectively means that we have to store this on every call or verify-return
opcode.
b) or we need to restrict this kind of mixed usage further, such that each
block may only contain declarations. I.e. while the above example would be
forbidden, one could still write
declare(strict_types=1) {
function foo() {}
}
declare(strict_types=0) {
function bar() {}
}
This would satisfy the given use-case, but it would also introduce a
somewhat arbitrary seeming limitation.
Nikita
It's not specifically me but Symfony's ClassCollectionLoader::load()
method, which generates a single file with common classes inlined for
faster bootstrapping (the perf gain is objectively measurable).
I have a hard time believing concatenating files is measurably faster with PHP 7 and a well-configured opcache.
-Rasmus
Hello,
back on this topic:
2016-09-08 6:51 GMT+02:00 Rasmus Lerdorf rasmus@lerdorf.com:
It's not specifically me but Symfony's ClassCollectionLoader::load()
method, which generates a single file with common classes inlined for
faster bootstrapping (the perf gain is objectively measurable).I have a hard time believing concatenating files is measurably faster with
PHP 7 and a well-configured opcache.
It took some time to confirm, but we're going to deprecate inlining on PHP
7!
We opened a thread on Symfony, and it looks like people are confirming that
they don't see any slow down when removing the inlining (they may even see
a small gain):
https://github.com/symfony/symfony/issues/20668
Thanks for challenging the statement!
Nicolas
Hi,
Nicolas Grekas wrote:
Thanks for trying Julien
I guess what you miss for what you want to do, is to detect if strict
types are activated into the current scope, at runtime.
From the inside, the author of the code knows if they added the declare or
not.
I'd need to know from the outside, before concatenating it, if some file
has strict types.
This could be exposed on the reflection, since a function/method/class
could have a flag that tells if it has been compiled with strict types
enabled or not.
The current alternative is to parse the source to check if it starts with
the declare directive (but not trivial because of non semantic tokens).Here is my current regex to do so:
$c = '(?:\s*+(?:(?:#|//)[^\n]+\n|/*(?:(?<!*/).)++)?+)+';
$strictTypesRegex = str_replace('.', $c, "'^<?php\s.declare.(.strict_
types.=.1.).;'is");
Reflection deals with classes, functions, and so on, but it doesn't deal
with files. Files are, for the most part, a detail that is only
represented when code is compiled (e.g. when an include or require
statement is run, or when a script is executed by a request or from the
command line). After that compilation stage, however, there's no
structure for a “file” in memory (beyond maybe in OPcache). The
constants, functions, classes and variables it defines are kept track of
by the PHP interpreter, but the file itself isn't directly.
Because of this, implementing a reflection method to tell you whether a
file uses the strict_types directive isn't currently possible. To the
best of my knowledge, PHP doesn't maintain a registry of files in memory
which tracks whether they use strict typing or not. Instead it marks any
code originating from strictly-typed files as using strict mode.
I suppose the PHP interpreter could be modified so it kept track of this
information. Though, if you're concatenating arbitrary source files, I
feel like reflection might be the wrong tool to use. I think reflection
is more intended for inspecting your own codebase, rather than analysing
arbitrary PHP files. In order to use reflection on some PHP file, you
have to include it first, and you can't un-include it later. You also
can't prevent any top-level PHP code in that file from executing when
you include it.
If you don't need to know whether a /file/ specifically uses strict
typing, and only need to know which classes do, that might be possible,
given PHP does know at runtime which methods use strict typing. Again,
though, I'm not sure if reflection is the best tool.
Your best hope is probably to try and parse the file using PHP-Parser or
the Tokenizer extension.
I hope this is helpful.
--
Andrea Faulds
https://ajf.me/
Hi,
Nicolas Grekas wrote:
Thanks for trying Julien
I guess what you miss for what you want to do, is to detect if strict
types are activated into the current scope, at runtime.
From the inside, the author of the code knows if they added the declare
or
not.
I'd need to know from the outside, before concatenating it, if some file
has strict types.
This could be exposed on the reflection, since a function/method/class
could have a flag that tells if it has been compiled with strict types
enabled or not.
The current alternative is to parse the source to check if it starts with
the declare directive (but not trivial because of non semantic tokens).Here is my current regex to do so:
$c = '(?:\s*+(?:(?:#|//)[^\n]+\n|/*(?:(?<!*/).)++)?+)+';
$strictTypesRegex = str_replace('.', $c, "'^<?php\s.declare.(.strict_types.=.1.).;'is");
Reflection deals with classes, functions, and so on, but it doesn't deal
with files. Files are, for the most part, a detail that is only represented
when code is compiled (e.g. when an include or require statement is run, or
when a script is executed by a request or from the command line). After that
compilation stage, however, there's no structure for a “file” in memory
(beyond maybe in OPcache). The constants, functions, classes and variables
it defines are kept track of by the PHP interpreter, but the file itself
isn't directly.Because of this, implementing a reflection method to tell you whether a file
uses the strict_types directive isn't currently possible. To the best of my
knowledge, PHP doesn't maintain a registry of files in memory which tracks
whether they use strict typing or not. Instead it marks any code originating
from strictly-typed files as using strict mode.I suppose the PHP interpreter could be modified so it kept track of this
information. Though, if you're concatenating arbitrary source files, I feel
like reflection might be the wrong tool to use. I think reflection is more
intended for inspecting your own codebase, rather than analysing arbitrary
PHP files. In order to use reflection on some PHP file, you have to include
it first, and you can't un-include it later. You also can't prevent any
top-level PHP code in that file from executing when you include it.If you don't need to know whether a /file/ specifically uses strict typing,
and only need to know which classes do, that might be possible, given PHP
does know at runtime which methods use strict typing. Again, though, I'm not
sure if reflection is the best tool.Your best hope is probably to try and parse the file using PHP-Parser or the
Tokenizer extension.I hope this is helpful.
I have to both, confirm and agree.
Julien
Hello,
It looks like we miss a way to check by reflection if a function/method has
strict types enabled or not.We'd need to do this in Symfony to generate a file with concatenated
classes, but split "declare_strict=1" classes out so that they don't break
(because there is no way to create a single file that contains both strict
and non-strict classes unfortunately also).Would it be possible to expose some flavor of ZEND_ACC_STRICT_TYPES to
userland?Regards,
Nicolas
As an aside, would you guys like to see a strict types indicator in debug
backtraces? I wrote a small patch for it back during development of 7, and
then forgot about it.
https://github.com/lt/php-src/commit/ac1171a81df814ad0042ef5989010bfe7d3e1652
As an aside, would you guys like to see a strict types indicator in debug
backtraces? I wrote a small patch for it back during development of 7, and
then forgot about it.https://github.com/lt/php-src/commit/ac1171a81df814ad0042ef5989010b
fe7d3e1652
+1. The more info exposed the better.
[...]
As an aside, would you guys like to see a strict types indicator in debug
backtraces? I wrote a small patch for it back during development of 7, and
then forgot about it.https://github.com/lt/php-src/commit/ac1171a81df814ad0042ef5989010bfe7d3e1652
Looks very useful. Maybe we want to add this to stack trace strings as well.
cu,
Lars