Missing reflection info about strict types?

php.internals

Nicolas Grekas

10 years ago
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

Niklas Keller

10 years ago
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.

Davey Shafik

10 years ago
> 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. This means you cannot reflect on it, unless we start doing reflection on source code files themselves. - Davey

Michael Morris

10 years ago
On Mon, Sep 5, 2016 at 5:49 AM, Davey Shafik <me@daveyshafik.com> wrote:
> > > 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.

Julien Pauli

10 years ago
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

Nicolas Grekas

10 years ago
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

Julien Pauli

10 years ago
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 patch https://github.com/jpauli/php-src/tree/is_strict_types Julien.P

Julien Pauli

10 years ago
On Mon, Sep 5, 2016 at 3:19 PM, Julien Pauli <jpauli@php.net> wrote:
> 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 patch > > https://github.com/jpauli/php-src/tree/is_strict_types
Any souls to +1 the reflection patch ? Julien

Niklas Keller

10 years ago
2016-09-05 15:52 GMT+02:00 Julien Pauli <jpauli@php.net>:
> On Mon, Sep 5, 2016 at 3:19 PM, Julien Pauli <jpauli@php.net> wrote: > > 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 patch > > > > https://github.com/jpauli/php-src/tree/is_strict > <https://github.com/jpauli/php-src/tree/is_strict_types>
> > *Julien*
> *--*
*PHP Internals - PHP Runtime Development Mailing List* *To unsubscribe, visit: http://www.php.net/unsub.php
> <http://www.php.net/unsub.php>*
_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

Pierre Joye

10 years ago
Hi, On Sep 5, 2016 8:53 PM, "Julien Pauli" <jpauli@php.net> wrote:
> > On Mon, Sep 5, 2016 at 3:19 PM, Julien Pauli <jpauli@php.net> wrote: > > 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 patch > > > > https://github.com/jpauli/php-src/tree/is_strict_types > > Any 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?

Nicolas Grekas

10 years ago
> > 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

Niklas Keller

10 years ago
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?

Nicolas Grekas

10 years ago
> 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

Davey

10 years ago
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

Rasmus Lerdorf

9 years ago
On Sep 5, 2016, at 13:13, Nicolas Grekas <nicolas.grekas@gmail.com> wrote:
> > 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

Andrew Faulds

10 years ago
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/

Julien Pauli

10 years ago
On Mon, Sep 5, 2016 at 7:38 PM, Andrea Faulds <ajf@ajf.me> wrote:
> 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

Leigh

10 years ago
On Mon, 5 Sep 2016 at 10:39 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? > > 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

Michael Morris

10 years ago
On Wed, Sep 7, 2016 at 4:13 AM, Leigh <leight@gmail.com> wrote:
> > > 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.

Lars Strojny

9 years ago
> On 07 Sep 2016, at 10:13, Leigh <leight@gmail.com> wrote: > > On Mon, 5 Sep 2016 at 10:39 Nicolas Grekas <nicolas.grekas@gmail.com> wrote:
[...]
> 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