{"id":41502924,"url":"https://github.com/frdl/remote-psr4","last_synced_at":"2026-01-23T19:11:51.738Z","repository":{"id":44497560,"uuid":"231001601","full_name":"frdl/remote-psr4","owner":"frdl","description":"An php psr4 Autoloader which is autoloading from a remote server.","archived":false,"fork":false,"pushed_at":"2025-01-31T22:47:35.000Z","size":1169,"stargazers_count":3,"open_issues_count":0,"forks_count":2,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-01-31T23:26:34.934Z","etag":null,"topics":["autoloading","classmap","php-psr4-autoloader","psr4"],"latest_commit_sha":null,"homepage":"https://webfan.de/install/","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/frdl.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-12-31T01:02:13.000Z","updated_at":"2025-01-31T22:47:38.000Z","dependencies_parsed_at":"2024-01-15T08:43:19.840Z","dependency_job_id":"6861196d-c531-40a2-9edc-639cf95f47d5","html_url":"https://github.com/frdl/remote-psr4","commit_stats":{"total_commits":265,"total_committers":1,"mean_commits":265.0,"dds":0.0,"last_synced_commit":"13d7e9995b55700ba8ad5fcd9c203ad5ce481519"},"previous_names":[],"tags_count":32,"template":false,"template_full_name":null,"purl":"pkg:github/frdl/remote-psr4","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/frdl%2Fremote-psr4","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/frdl%2Fremote-psr4/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/frdl%2Fremote-psr4/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/frdl%2Fremote-psr4/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/frdl","download_url":"https://codeload.github.com/frdl/remote-psr4/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/frdl%2Fremote-psr4/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28698424,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-23T17:25:48.045Z","status":"ssl_error","status_checked_at":"2026-01-23T17:25:47.153Z","response_time":59,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["autoloading","classmap","php-psr4-autoloader","psr4"],"created_at":"2026-01-23T19:11:50.869Z","updated_at":"2026-01-23T19:11:51.732Z","avatar_url":"https://github.com/frdl.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# remote-psr4\nAn php **PSR-4**[[?]](https://www.php-fig.org/psr/psr-4/) Autoloader `Client` which is autoloading from one or more remote servers.\nWith remote **Classmap** and **Aliases**.\n\n### Server?\nIf you want to build an `API Server` for this client, you can install my Package [`frdl/codebase`](https://github.com/frdl/codebase).\n\nThis is recommended for larger projects, **please use [my API Server](https://webfan.de/install/) only for testing, \nif want to use it productionaly or need a larger amount of load, please contact me!**\nYou can also [contact me](https://startforum.de/u/till.wehowski/about) if you need any help with my packages, or if you need a [webhosting plan](https://domainundhomepagespeicher.de/)!\n\nOptionaly you can [configure the client](#with-custom-configuration), e.g. to load from githubs **without a central server**.\n\n## Usage\n````php\n  $loader = \\frdl\\implementation\\psr4\\RemoteAutoloader::getInstance('03.webfan.de', true, 'latest', true);\n````\n...or...\n## With (custom) configuration\n* Classmap (with PSR4 and Alias)\n* Cache Settings\n````php\n$config = [\n    'FRDL_UPDATE_CHANNEL' =\u003e 'latest',  // stable | latest\n    'FRDL_REMOTE_PSR4_CACHE_DIR'=\u003e \\sys_get_temp_dir().\\DIRECTORY_SEPARATOR. \\get_current_user()\n\t\t\t\t                       .\\DIRECTORY_SEPARATOR.'.frdl'.\\DIRECTORY_SEPARATOR.'runtime'.\\DIRECTORY_SEPARATOR.'cache'.\\DIRECTORY_SEPARATOR\n\t\t\t                         .'classes'.\\DIRECTORY_SEPARATOR.'psr4'.\\DIRECTORY_SEPARATOR,    \n     'FRDL_REMOTE_PSR4_CACHE_LIMIT'=\u003e\t24 * 60 * 60,                                \n];\n\n // $workspace is the DEFAULT Psr4 Server\n // e.g.: $workspace = 'https://webfan.de/install/'. $config['FRDL_UPDATE_CHANNEL'].'/?source=${class}\u0026salt=${salt}\u0026source-encoding=b64'\n $loader =  call_user_func(function($config,$workspace){\n    return \\frdl\\implementation\\psr4\\RemoteAutoloaderApiClient::getInstance($workspace, false, $config['FRDL_UPDATE_CHANNEL'], false, false, \n\t/*Classmap (with PSR4 and Alias)*/\n       [\n       // (CLASSMAP) Concrete Classes:     \n        \\frdlweb\\Thread\\ShutdownTasks::class =\u003e 'https://raw.githubusercontent.com/frdl/shutdown-helper/master/src/ShutdownTasks.php',\n        \\Webfan\\Webfat\\Jeytill::class =\u003e 'https://raw.githubusercontent.com/frdl/webfat-jeytill/main/src/Jeytill.php',\t  \n\t    \n       // (PSR4) NAMESPACES   = \\\\ at the end:\n      'frdl\\\\Proxy\\\\' =\u003e 'https://raw.githubusercontent.com/frdl/proxy/master/src/${class}.php?cache_bust=${salt}',    \t    \n    \n      // ALIAS = @ as first char:\n      '@Webfan\\\\Autoloader\\\\Remote' =\u003e __CLASS__,\t \n      // ALIAS works with PSR4-Namespace too:   \n      '@BetterReflection\\\\'=\u003e'Roave\\BetterReflection\\\\',  \n      \n      //Versions at Webfan:\n\t  // Default/Fallback Versions Server:\n\t\\webfan\\hps\\Format\\DataUri::class =\u003e 'https://webfan.de/install/?salt=${salt}\u0026source=webfan\\hps\\Format\\DataUri',\t    \n\t // Stable/Current Versions Server:   \n        //\\webfan\\hps\\Format\\DataUri::class =\u003e 'https://webfan.de/install/stable/?salt=${salt}\u0026source=webfan\\hps\\Format\\DataUri',\t    \n\t// Latest/Beta Versions Server:    \n\t// \\webfan\\hps\\Format\\DataUri::class =\u003e 'https://webfan.de/install/latest/?salt=${salt}\u0026source=webfan\\hps\\Format\\DataUri',\n   ],\n    $config['FRDL_REMOTE_PSR4_CACHE_DIR'],\n    $config['FRDL_REMOTE_PSR4_CACHE_LIMIT']\n  );\n }, $config,'https://webfan.de/install/'. $config['FRDL_UPDATE_CHANNEL'].'/?source=${class}\u0026salt=${salt}\u0026source-encoding=b64');\n ````\n *more examples...*\n ````php\n /** More Examples: */\n $loader-\u003ewithClassmap([\n//Alias:\n\t\t'@'.\\BetterReflection\\Reflection\\ReflectionFunction::class =\u003e \\Roave\\BetterReflection\\BetterReflection::class,   \n\t\t'@'.\\BetterReflection\\SourceLocator\\Exception\\TwoClosuresOneLine::class =\u003e \n\t\t    \\Roave\\BetterReflection\\SourceLocator\\Exception\\TwoClosuresOnSameLine::class,   \t\t    \n   \t        \\Webfan\\Webfat\\MainModule::class =\u003e \n\t\t    'https://raw.githubusercontent.com/frdl/recommendations/master/src/Webfan/Webfat/MainModule.php?cache_bust=${salt}',\n//Conrete class AND namespace: ORDER matters!\n//Classmaps SHOULD be sorted DESC in most cases, CLASS should be listed BEFORE namespace if equal FQN.\n\t\t\\Webfan\\Webfat\\Module::class =\u003e 'https://raw.githubusercontent.com/frdl/recommendations/master/src/Webfan/Webfat/Module.php?cache_bust=${salt}',\n\t\t'Webfan\\Webfat\\Module\\\\' =\u003e 'https://raw.githubusercontent.com/frdl/recommendations/master/src/Webfan/Webfat/Module/${class}.php?cache_bust=${salt}', \n\t\t'Webfan\\Webfat\\Intent\\\\' =\u003e 'https://raw.githubusercontent.com/frdl/recommendations/master/src/Webfan/Webfat/Intent/${class}.php?cache_bust=${salt}',    \t\t \\Pimple::class =\u003e 'https://raw.githubusercontent.com/silexphp/Pimple/1.1/lib/Pimple.php',\n\t\t'Task\\Plugin\\Console\\\\' =\u003e 'https://github.com/taskphp/console/blob/00bfa982c4502938ca0110d2f23c5cd04ffcbcc3/src/${class}.php?cache_bust=${salt}',\n\t\t'Task\\\\' =\u003e 'https://raw.githubusercontent.com/taskphp/task/7618739308ba484b5f90a83d5e1a44e1d90968d2/src/${class}.php?cache_bust=${salt}',\n\t\t'@BetterReflection\\\\'=\u003e'Roave\\BetterReflection\\\\',    \n\t    ]); \n ````\n \n### Register Autoloader\n````php\n$loader-\u003eregister(false);\n// Prepend autoloader to autoloader-stack:\n// $loader-\u003eregister(true);\n ````\n**Prepend Autoloader:** If you like to prepend the autoloader you have to set the public static variable *allwaysAppendAutoloader* to *false*, **before** registering the autoloader!\n````PHP\npublic static $alwaysAppendLoader = true;\n````\n````PHP\n\\frdl\\implementation\\psr4\\RemoteAutoloader::$alwaysAppendLoader = $loader::$alwaysAppendLoader = false;\n$loader-\u003eregister(true);\n````\nThis is **not recommended** as this loader loads from remote, for performance you should prefer your local classes (in production e.g.).\n\n## With (custom) validators\n[See methods:](https://github.com/frdl/remote-psr4/blob/master/src/implementations/autoloading/RemoteAutoloaderApiClient.php)\n* -\u003ewithBeforeMiddleware()\n* -\u003ewithAfterMiddleware()\n* -\u003ewithWebfanWebfatDefaultSettings()\n````PHP\n//...\n\n  $publicKeyChanged = false;\n  $increaseTimelimit = true;\n\n $setPublicKey = function($baseUrl,$expFile, $pubKeyFile){\n\t if(file_exists($expFile)){\n          $expires = intval(file_get_contents($expFile));\n\t }else{\n           $expires = 0;\n\t }\n\t\n\t   if($expires \u003e 0 \u0026\u0026 ($expires === time() || ($expires \u003e time() - 3 \u0026\u0026 $expires \u003c time() + 3))){\n\t\t   sleep(3);\n\t   }\n      if($expires \u003c= time()  || !file_exists($pubKeyFile) ){\n\t\t  \t$opts =[\n        'http'=\u003e[\n            'method'=\u003e'GET',\n            //'header'=\u003e\"Accept-Encoding: deflate, gzip\\r\\n\",\n            ],\n\t\n\t\t\t];\n\t\t  $context = stream_context_create($opts);\n\t\t  $key = file_get_contents($baseUrl.'source=@server.key', false, $context);\n\t\t  foreach($http_response_header as $i =\u003e $header){\t\t\t\t\n                   $h = explode(':', $header);\n\t\t\tif('x-frdlweb-source-expires' === strtolower(trim($h[0]))){\n\t\t\t\tfile_put_contents($expFile, trim($h[1]) );\n\t\t\t\tbreak;\n\t\t\t}           \n         }\n\t\t  \n\t\t  file_put_contents($pubKeyFile, $key);\n\t  }\n\t \n };\n\n $getDefaultValidatorForUrl = function($baseUrl, $cacheDir, $increaseTimelimit = true) use($setPublicKey, \u0026$publicKeyChanged) {\n     $expFile =  rtrim($cacheDir, '\\\\/ ') .\t\\DIRECTORY_SEPARATOR.'validator-'.sha1($baseUrl).strlen($baseUrl).'.expires.txt';\n\t $pubKeyFile =  rtrim($cacheDir, '\\\\/ ') .\t\\DIRECTORY_SEPARATOR.'validator-'.sha1($baseUrl).strlen($baseUrl).'.public-key.txt';\n\t \n     $setPublicKey($baseUrl,$expFile, $pubKeyFile);\n\n\t $condition = function($url, \u0026$loader, $class) use($baseUrl, $increaseTimelimit){\n\t\tif($increaseTimelimit){\n\t\t\tset_time_limit(min(180, intval(ini_get('max_execution_time')) + 90));\n\t\t}\n\n\t\tif($baseUrl === substr($url, 0, strlen($baseUrl) ) ){\n\t\t\treturn true;\t  \n\t\t}else{\n\t\t  return false;\t\n\t\t}\n\t };\n\t\n\t \n\t \n     $cb = null; \n     $filter = function($code, \u0026$loader, $class, $c = 0) use(\u0026$cb, $baseUrl, $expFile, $pubKeyFile, $setPublicKey, \u0026$publicKeyChanged) {\n\t        $c++;\n\t\t$sep = 'X19oYWx0X2NvbXBpbGVyKCk7'; \n        $my_signed_data=$code;\n        $public_key = file_get_contents($pubKeyFile);\n\t\t \n    list($plain_data,$sigdata) = explode(base64_decode($sep), $my_signed_data, 2);\n    list($nullVoid,$old_sig_1) = explode(\"----SIGNATURE:----\", $sigdata, 2);\n    list($old_sig,$ATTACHMENT) = explode(\"----ATTACHMENT:----\", $old_sig_1, 2);\n\t $old_sig = base64_decode($old_sig);\t \n\t $ATTACHMENT = base64_decode($ATTACHMENT);\n    if(empty($old_sig)){\n      return new \\Exception(\"ERROR -- unsigned data\");\n    }\n    \\openssl_public_decrypt($old_sig, $decrypted_sig, $public_key);\n    $data_hash = sha1($plain_data.$ATTACHMENT).substr(str_pad(strlen($plain_data.$ATTACHMENT).'', 128, strlen($plain_data.$ATTACHMENT) % 10, \\STR_PAD_LEFT), 0, 128);\n    if($decrypted_sig === $data_hash \u0026\u0026 strlen($data_hash)\u003e0){\n        return $plain_data;\n\t}else{\n\t\tif(!$publicKeyChanged \u0026\u0026 $c \u003c= 1){\n\t\t   $publicKeyChanged = true;\n\t\t   unlink($pubKeyFile);\n\t\t   unlink($expFile);\n\t\t   $setPublicKey($baseUrl, $expFile, $pubKeyFile);\n\t\t   return $cb($code, $loader, $class, $c);\t\n\t\t}\n        return new \\Exception(\"ERROR -- untrusted signature\");\n\t}\n  };\n    $cb = $filter;\n\t \n   return [$condition, $filter];\n };\n\n\n $getDefaultValidators = function($cacheDir, $increaseTimelimit = true) use($getDefaultValidatorForUrl) {\n    return [\n         $getDefaultValidatorForUrl('https://webfan.de/install/stable/?', $cacheDir, $increaseTimelimit),\n         $getDefaultValidatorForUrl('https://webfan.de/install/latest/?', $cacheDir, $increaseTimelimit),\n  \t $getDefaultValidatorForUrl('https://webfan.de/install/?', $cacheDir, $increaseTimelimit),\n    ];\n };\n\n\n     foreach($getDefaultValidators($cacheDir, $increaseTimelimit) as $validator){\n\t    $loader-\u003ewithAfterMiddleware($validator[0], $validator[1]);\n     }\t\t\n     \n     \t  $loader-\u003ewithBeforeMiddleware(function($class, \u0026$loader){\n\t       switch($class){\n\t\t       case \\DI\\Compiler\\Compiler::class :\n\t\t\t       $aDir = dirname($loader-\u003efile($class));\n\t\t\t       if(!is_dir($aDir)){\n\t\t\t\t  mkdir($aDir, 0755, true);       \n\t\t\t       }\n\t\t\t       $aFile = $aDir.\\DIRECTORY_SEPARATOR.'Template.php';\n\t\t\t       if(!file_exists($aFile)){\n\t\t\t\t  file_put_contents($aFile, file_get_contents('https://raw.githubusercontent.com/PHP-DI/PHP-DI/master/src/Compiler/Template.php'));     \n\t\t\t       }\n\t\t\t       return true;\n\t\t\t   break;\n\t\t       default:\n\t\t\t    return true;\n\t\t\t  break;\n\t       }\n\t   \n\t    /*   return true;  return false to skip this autoloader, return any/VOID to continue */\n          });     \n//...     \n````\n## Presets per App and PHP Version\nTo bundle presets and cache them, to load faster in the next requests you can use the method\n* **-\u003egetClassmapFor**\nThe Webfan API provides an endpoint to fetch bundled classmaps with the classes of an app/group and for a specific `PHP Version`.\nCurrently Frdlweb will possible deprecate PHP 7 support for new products, but possible will support \u003e=7+ and \u003e=8+.\nThe $version parameter should be enum of `latest`, `stable` and `legacy`.\nThe $app parameter `default` or `*` bundles most of the Framework classes, further you currently can specify the apps\n+ io4\n+ frdl/codebase | codebase\n+ webfan | webfat | webfan-website | webfan-webfat\n````PHP\npublic function getClassmapFor(string $app, string $version, string $phpVersion = \\PHP_VERSION, int | bool $cache = true) : array | bool\n````\nRequsts the following API endpoint:\n`\ncurl -X 'GET' \\\n  'https://api.webfan.de/v1/install/generate/default/latest/autoloading/remote-mapping/8.2/classmap' \\\n  -H 'accept: */*'\n\n`\n\nA successful response looks like (classmap in result member)\n````JSON\n{\n  \"code\": 200,\n  \"message\": \"Your (remote-fetcher-) classmap was generated in the result property.\",\n  \"for\": {\n    \"app\": \"default\",\n    \"version\": \"latest\",\n    \"php_version\": \"8.2\"\n  },\n  \"result\": {\n    \"@frdl\\\\Facades\": \"Webfan\\\\FacadesManager\",\n    \"Webfan\\\\ComposerAdapter\\\\\": \"https://raw.githubusercontent.com/frdl/composer-adapter/master/src/${class}.php?cache_bust=${salt}\",\n    \"Webfan\\\\Codebase\\\\Server\\\\BundleExportHelper\": \"https://webfan.de/install/?source=Webfan\\\\Codebase\\\\Server\\\\BundleExportHelper\u0026salt=${salt}\",\n    \"WMDE\\\\VueJsTemplating\\\\\": \"https://raw.githubusercontent.com/wmde/php-vuejs-templating/2.0.0/src/${class}.php?cache_bust=${salt}\",\n    \"   ...   \": \"   ...   \"\n  }\n}\n````\nTo load the classmap corresponding to the PHP version you are running, from cache or API, and\nattatch the classmaps autoload definitions to the $loader immediately, you can use the method\n* **-\u003ewithClassmapFor**\n````PHP\nwithClassmapFor(string $app, string $version, string $phpVersion = \\PHP_VERSION, int | bool $cache = true) \n````\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffrdl%2Fremote-psr4","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffrdl%2Fremote-psr4","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffrdl%2Fremote-psr4/lists"}