On 06/04/2021 02:00, Jesse Rushlow wrote:
As an example, when the user runs "make:command"; we check if they
have PHP 8 && if "class_exists(Symfony\.....\Attributes::class)" - if both
of those === true, we import the attribute class and generate the needed
attributes for the command. Otherwise we would fall back to using
annotations if the user is running PHP 7. Where I ran into trouble was the
Attributes::class (defines the attributes to be used in userland) utilized
constructor property promotion (which is a pretty sweet addition to 8 imo).
The way you write it here, there is no problem: you first check if the
user is running PHP >= 8.0 (to see if you should use native attributes)
and only if they are do you check if the Attributes class is available.
I would expect any of the \*_exists() to tell me if
the class/method/interface exists regardless of the implementation.
As far as the PHP run-time is concerned, there is no definition of
"exists" that doesn't involve an implementation. It might help to step
through what actually happens in this case:
* class_exists() first checks if the class is already defined; it's not,
and you've left $autoload as true, so it calls the autoloader
* the autoloader is just a callback function which is given the desired
class name; generally, it translates that to a file name, and calls
include/require
* the autoloader asked PHP to include a particular file, so PHP tries to
parse that file
* as far as PHP 7.4 is concerned, the file contains a syntax error
* since the file didn't compile, the class was never defined, so it
continues to not exist
Note that PHP 7.4 doesn't know that this is a PHP 8.0 class definition;
it just sees it as invalid PHP. In the same way, a file containing the
keyword "enum" has a syntax error under 8.0 but will be valid in 8.1;
while one with typed properties has a syntax error under 7.3 but is
valid from 7.4 onwards - I'm pretty sure I could come up with an example
for every PHP release.
My first thought would be if the implementation is not compatible with the PHP
version some sort of \RuntimeException::class would be thrown by PHP.
This is in fact exactly what happens: a ParseError is thrown, and can be
caught like any other exception. However, I'm not clear what you'd do
when you caught it, which is why I was hoping to see an example of the
code you were actually trying to write.
By far the most common solution to this problem is to mark the required
PHP version at the package level: if you have files using PHP 8.0
syntax, the package containing them shouldn't be installed on a PHP 7.4
system. If they're not installed, the autoloader can't try to load them,
and your class_exists() check will cleanly return false.
If you do want to mix files for different PHP versions in one package,
you could do it with a custom autoloader - e.g. all the PHP 8 only
classes could be in a "src-php8" and the rest in "src"; the autoloader
would check PHP_ VERSION_ID and simply ignore the "src-php8" directory
for older versions. Again, the class_exists() check would cleanly return
false.
If you want to be able to generate PHP 8.0 code while running under PHP
7.4, then something like the library Marco linked to would seem to be
the solution.
Regards,
--
Rowan Tommins
[IMSoP]
--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: https://www.php.net/unsub.php