Composer – modifying autoloading SRC at run time based on PHP version – Code Utility

[

I was wondering if there is a way to load different folder based on php version. I could do it writing my own autoloader, but i was wondering if there is a way of using composer for that?

Ps. I have seen this in use before in module / plugin application that where redistributed globally to work with wide range of env. Those scripts where using own autoloading classes.

I am curios is there a way to use composer in similar way.

Scenario: class / folder structure:

class >
   php5.6 >
      - SomeClass.php
      ...
   php7.x >
      - SomeClass.php
      ...
   php8.x >
      - SomeClass.php
      ...

Compare php version and do something:

$classPathForAutoloader = '';
if (version_compare(PHP_VERSION, '8.0.0') >= 0) {
  $classPathForAutoloader = 'php8.x';
  // do something to composer autoload or 
  // use declaration
}else if(version_compare(PHP_VERSION, '7.0.0') >= 0){
  $classPathForAutoloader = 'php7.x';
  // do something to composer autoload or 
  // use declaration
}else if(version_compare(PHP_VERSION, '5.6.0') >= 0{
  $classPathForAutoloader = 'php5.6';
  // do something to composer autoload or 
  // use declaration
}else{
    // throw Exception ...
}

standard composer setup:

{
    "name": "some/name",
    "require": {
    },
    "authors": [
        {
            "name": "Some Name",
            "email": "[email protected]"
        }
    ],
    "autoload": {
        "psr-4": {
            "Devwl\\": "class/",
            "Tools\\": "tools/"           
        },
        "classmap": [
            "class/"
          ],
        "exclude-from-classmap": []
    }
}

,

I don’t think Composer provides a way to dynamically autoload different paths.

Can you use version_compare in a single SomeClass.php class only where the functionality differs by PHP version? Or write the entire SomeClass.php to be backwards compatible?

To me loading different classes depending on the PHP version is asking for trouble when it comes to reproducibility across environments.

Another option would be to use require_once to load the different classes, but for maintainability I’d really lean towards a single class with version checks only when absolutely necessary.

,

So i have created a class which does what I need. It can laso work alongside Composer autoloader.

<?php

class PhpVersionAutoloader{
  private $baseClassesDirPath = null;
  private $phpVersionArr = [];
  private $phpDir = null;
  private $classes = []; /** Keep a record of all loaded classes */

  /**
   * Allows to change base dir path.
   * If not set the path will be set to file _DIR_
   * 
   * @see $this->baseClassesDirPath
   *
   * @param string $path
   * @return void
   */
  public function setBaseClassesDirPath($path)
  {
    $this->baseClassesDirPath = $path;
  }

  /**
   * Map available dir name and php version
   * 
   * @see $this->phpVersionArr
   *
   * @param string $directory name 
   * @param string $phpVersion
   * @return void
   */
  public function registerPhpDir($dir, $phpVersion){
    $this->phpVersionArr[] = [$dir => $phpVersion];
  }

  /**
   * Compare curent php version with  $this->phpVersionArr to determin the right path for class load
   */
  public function selectPhpDir(){

    foreach ($this->phpVersionArr as $key => $phpVDir) {
      $this->position = $key;
      foreach($phpVDir as $key => $value){
        if (version_compare(PHP_VERSION, $value) >= 0){
          $this->phpDir = $key;
          break 2;
        }
      }
    }
  }

  /**
   * Register autloader
   *
   * @return void
   */
  public function register(){
    spl_autoload_register(function($className)
    {
        $namespace = str_replace("\\","/",__NAMESPACE__);
        $className = str_replace("\\","/",$className);

        $this->baseClassesDirPath = ($this->baseClassesDirPath === null) ? str_replace("\\","/",__DIR__) : $this->baseClassesDirPath;

        $class = $this->baseClassesDirPath."/classes/".$this->phpDir.'/'. (empty($namespace)?"":$namespace."/")."{$className}.php";
        $this->classes[] = $class;
        
        if (file_exists($class)){
          include_once($class);
        }else{
          // ... if not exsist try to load lower php version file?
          // ... or throw new Error("Error Processing Request", 1);
          
        }
    });
  }

}

Use the PhpVersionAutoloader object like this:

/**
 * Use example
 */
$loader = new PhpVersionAutoloader(); // init PhpVersionAutoloader object
$loader->setBaseClassesDirPath('C:/xampp/htdocs/blog/blog autoloading'); // if not used will use _DIR_ to create path

$loader->registerPhpDir('php8.x', '8.0.0'); // as "folder name" => "php version" 
$loader->registerPhpDir('php7.x', '7.0.0'); // ...
$loader->registerPhpDir('php5.6', '5.6.0'); // ...

$loader->selectPhpDir(); // compare system php version and selects the correct phpX.X subfolder
$loader->register(); // register autoloader

I also created more functional class of this loader which allow to force specific directory to load from or even allow to load classes from older version of PHP if not found in selected version.

See github [here]

]