2

Does anyone know how to terminate PHP script with too low unsupported version before an error occurs in the same script (due to calling an unsupported function/feature)?

Heres an example with readonly class which was introduced in PHP 8.2:

#!/usr/bin/env php
<?php

define("MIN_PHP_VER", 8.2);

if (version_compare(PHP_VERSION, MIN_PHP_VER, '<'))
{ 
    echo "This script requires at least PHP " . MIN_PHP_VER . "\n"; 
    die();
}

readonly class SomeClass
{
    
}

This won't work, obviously, because the entire script is read at the beginning, so in PHP < 8.2 it will thrown an error:

syntax error, unexpected token "readonly"

I would like to make the script execution dependent on the PHP version available in a given environment without having to check it with another script (everything is executed within one script), so expected output should be:

This script requires at least PHP 8.2

Is it somehow possible to achieve?

I know that I can use composer or two scripts (the first one will execute second one when PHP_VERSION match requirements). But I just want to have one script that will verbose explains to the user why he can't use it (or it will just execute if the PHP version is right).

Andrew
  • 1,386
  • 1
  • 9
  • 8
  • 2
    I could be wrong but I don't see how this would be possible, for exactly the reason you mention (at least in the case of syntax incompatibility, anyway). At least the script is still terminated, at any rate, just not in the manner you were hoping for! – ADyson Jul 28 '23 at 14:18
  • 1
    If you want to support 2 or more versions of PHP in your code you must follow backwards compatibility conventions which means that you must ensure that older versions work. Therefore you can't use anything in your code that isn't in the older versions. – Flash Jul 28 '23 at 14:22
  • 1
    If this is a pattern you actively need, then I think you will need to use multiple files and conditionally load them. That's how Symfony's polyfill works: https://github.com/symfony/polyfill/blob/main/src/bootstrap.php. Obviously, weigh the benefits of the features you are adding vs the maintenance of multiple similar versions of the same code – Chris Haas Jul 28 '23 at 14:41
  • @Flash I do not want to support 2+ PHP versions. I just want to warn the user, that my script requires a specific version of PHP. And keep everything in one script. – Andrew Jul 28 '23 at 16:06
  • You can't keep everything in one script, as you've already proved. You'll have to compromise. [Sylwester's answer](https://stackoverflow.com/a/76788827/5947043) is probably the closest you'd get I think (and I'd say makes sense in terms of separation of concerns anyway). What's the problem with having 2 files instead of 1? – ADyson Jul 28 '23 at 16:09
  • 1
    If you're absolutely insistent on having a single file then you have only two options -- run everything but the version check inside a big `eval()` (but don't do that) or build a [PHAR](https://www.php.net/manual/en/intro.phar.php) that contains multiple embedded source files. – Alex Howansky Jul 28 '23 at 16:30

2 Answers2

2

PSR1, which is expanded by both PSR-2 and PSR-12, dictates you can have a file declare new symbols, like function and classes, or you can have one with side effects. You should not have a file that does both. Your script does both!

You should do this:

<?php
define("MIN_PHP_VER", 8.2);
if (version_compare(PHP_VERSION, MIN_PHP_VER, '<'))
{ 
    echo "This script requires at least PHP " . MIN_PHP_VER . "\n"; 
    die();
}

require __DIR__ . "/vendor/autoload.php";
startMeUp($args);

So now you have more than one file, but there is a solution to make it one executable phar archive. Eg. composer is an example of an application that is made like that.

Sylwester
  • 47,942
  • 4
  • 47
  • 79
1

The typical way to handle this issue would be to use Composer's require configuration to constrain the version at install time. This implies the use of PSR-4 naming conventions, to enable the use of an autoloader -- which you should be doing anyway. Given that, you can then just specify your version requirements in the composer.json file, and you won't need to do any explicit version checking anywhere in your code:

{
   "require": {
      "php": "^8.2"
   }
}

Then when you run composer install or composer update, you'll get an error:

Loading composer repositories with package information
Updating dependencies
Your requirements could not be resolved to an installable set of packages.

  Problem 1
    - Root composer.json requires php ^8.2 but your php version (8.0.29) does not satisfy that requirement.
Alex Howansky
  • 50,515
  • 8
  • 78
  • 98