PHP Classes

File: src/implementations/autoloading/RemoteAutoloaderApiClient.php

Recommend this page to a friend!
  Classes of Till Wehowski   Remote PHP PSR 4 Class Loader   src/implementations/autoloading/RemoteAutoloaderApiClient.php   Download  
File: src/implementations/autoloading/RemoteAutoloaderApiClient.php
Role: Class source
Content type: text/plain
Description: Class source
Class: Remote PHP PSR 4 Class Loader
PSR-4 compliant loader for remote classes
Author: By
Last change: Update RemoteAutoloaderApiClient.php

+ removed Waynes
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php

fixed: $http_response_header may not be isset
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php

+ public function withTimeout(int $timeout)
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Update RemoteAutoloaderApiClient.php
Date: 6 months ago
Size: 78,328 bytes
 

Contents

Class file image Download
<?php /** * Bundled from https://github.com/frdl/codebase/tree/main/src/Frdlweb/Contract/Autoload * @ToDo: How could I "preload" this better/from github-sources? * https://raw.githubusercontent.com/frdlweb/webfat/main/public/index.php * @ToDo StubModuleBuilder **/ namespace Frdlweb\Contract\Autoload{ if (!\interface_exists(CodebaseInterface::class, false)) { interface CodebaseInterface { const ALL_CHANNELS = '*'; const ENDPOINT_DEFAULT = 'RemoteApiBaseUrl'; const ENDPOINT_PROVIDER_IDENTITY_CENTRAL = 'io4.pid.central'; const ENDPOINT_WEBFAT_CENTRAL = 'io4.webfat.central'; const ENDPOINT_REMOTE_PUBLIC = 'io4.workspace.public'; const ENDPOINT_REMOTE_PRIVATE = 'io4.workspace.private'; const ENDPOINT_WORKSPACE_REMOTE = 'io4.workspace.remote'; const ENDPOINT_INSTALLER_REMOTE = 'io4.installer.remote'; const ENDPOINT_PROXY_OBJECT_REMOTE = 'io4.proxy-object.remote'; const ENDPOINT_CONTAINER_REMOTE = 'io4.container.remote'; const ENDPOINT_CONFIG_REMOTE = 'io4.config.remote'; const ENDPOINT_MODULES_WEBFANSCRIPT_REMOTE = 'RemoteModulesBaseUrl'; const ENDPOINT_AUTOLOADER_PSR4_REMOTE = 'RemotePsr4UrlTemplate'; const ENDPOINT_UDAP = 'io4.udap'; const ENDPOINT_RDAP = 'io4.rdap'; const ENDPOINT_OIDIP = 'io4.rdap'; const CHANNEL_LATEST = 'latest'; const CHANNEL_STABLE = 'stable'; const CHANNEL_FALLBACK = 'fallback'; const CHANNEL_TEST = 'test'; const CHANNELS =[ self::CHANNEL_LATEST => self::CHANNEL_LATEST, self::CHANNEL_STABLE => self::CHANNEL_STABLE, self::CHANNEL_FALLBACK => self::CHANNEL_FALLBACK, self::CHANNEL_TEST => self::CHANNEL_TEST, ]; const DEFAULT_ENDPOINT_NAMES =[ self::ENDPOINT_DEFAULT, self::ENDPOINT_PROVIDER_IDENTITY_CENTRAL, self::ENDPOINT_WEBFAT_CENTRAL, self::ENDPOINT_REMOTE_PUBLIC, self::ENDPOINT_REMOTE_PRIVATE, self::ENDPOINT_WORKSPACE_REMOTE, self::ENDPOINT_INSTALLER_REMOTE, self::ENDPOINT_MODULES_WEBFANSCRIPT_REMOTE, self::ENDPOINT_PROXY_OBJECT_REMOTE, self::ENDPOINT_CONTAINER_REMOTE, self::ENDPOINT_AUTOLOADER_PSR4_REMOTE, self::ENDPOINT_UDAP, self::ENDPOINT_RDAP, self::ENDPOINT_OIDIP, self::ENDPOINT_CONFIG_REMOTE, ]; public function loadUpdateChannel(mixed $StubRunner = null) : string; public function getRemoteApiBaseUrl(?string $serviceEndpoint = self::ENDPOINT_DEFAULT) : string|bool; public function setUpdateChannel(string $channel); public function getUpdateChannel() : string; public function getRemotePsr4UrlTemplate() : string; public function getRemoteModulesBaseUrl() : string; public function getServiceEndpoints() : array; public function getServiceEndpointNames() : array; public function setServiceEndpoints(array $serviceEndpoints) : CodebaseInterface; public function setServiceEndpoint(string $serviceEndpointName, string|\Closure|\callable $baseUrl, ?string $channel = self::ALL_CHANNELS) : CodebaseInterface; } } } namespace Frdlweb\Contract\Autoload{ if (!interface_exists(ClassmapGeneratorInterface::class)) { interface ClassmapGeneratorInterface { public function addDirectory(string $dir); } } }//ns Frdlweb\Contract\Autoload namespace Frdlweb\Contract\Autoload { if (!interface_exists(SourceCodeGeneratorInterface::class)) { interface SourceCodeGeneratorInterface { public function file(string $className):string; public function source(string $className):string; public function bundle(array $classes):string; } } } namespace Frdlweb\Contract\Autoload{ if (!interface_exists(GeneratorInterface::class)) { interface GeneratorInterface { public function withContext(Context $Context); public function withPackage(string | array | object $urlPackageNameOrComposerJson ); public function withDirectory($dir); public function withAlias(string $alias, string $rewrite); public function withClassmap(array $classMap = null); public function withNamespace($prefix, $server, $prepend = false); } } }//ns Frdlweb\Contract\Autoload namespace Frdlweb\Contract\Autoload{ if (!interface_exists(Psr4GeneratorInterface::class)) { interface Psr4GeneratorInterface { public function addNamespace($prefix, $resourceOrLocation, $prepend = false); } } }//ns Frdlweb\Contract\Autoload namespace Frdlweb\Contract\Autoload{ if (!interface_exists(ClassmapGeneratorApiInterface::class)) { interface ClassmapGeneratorApiInterface { public function getClassmapCachefileFor(string $app, string $version, string $phpVersion = \PHP_VERSION) : string; // $cache = true : load from cache if exists // $cache = int : load from cache if it is newer than $cache seconds // $cache = false : invalidate cache for the classmap and load from implementation/API public function getClassmapFor(string $app, string $version, string $phpVersion = \PHP_VERSION, int | bool $cache = true) : array | bool; public function withClassmapFor(string $app, string $version, string $phpVersion = \PHP_VERSION, int | bool $cache = true) : bool; } } }//ns Frdlweb\Contract\Autoload namespace Frdlweb\Contract\Autoload{ if (!interface_exists(RemoteClassmapGeneratorInterface::class)) { interface RemoteClassmapGeneratorInterface { public function withClassmap(array $classMap = null); } } }//ns Frdlweb\Contract\Autoload namespace Frdlweb\Contract\Autoload{ if (!interface_exists(AliasMapGeneratorInterface::class)) { interface AliasMapGeneratorInterface { public function withAlias(string $alias, string $rewrite); } } }//ns Frdlweb\Contract\Autoload namespace Frdlweb\Contract\Autoload{ if (!interface_exists(ClassLoaderInterface::class)) { interface ClassLoaderInterface { public function Autoload(string $class):bool|string; } } }//ns Frdlweb\Contract\Autoload namespace Frdlweb\Contract\Autoload{ if (!interface_exists(ResolverInterface::class)) { interface ResolverInterface { public function resolve(string $class):bool|string; public function file(string $class):bool|string; //local or cache public function url(string $class):bool|string; //remote or ...? } } }//ns Frdlweb\Contract\Autoload namespace Frdlweb\Contract\Autoload{ if (!interface_exists(LoaderInterface::class)) { interface LoaderInterface { // public const HAS_ALWAYS_CORRECT_STACK_ORDER = false; public function register(bool $prepend = false); } } }//ns Frdlweb\Contract\Autoload namespace Frdlweb\Contract\Autoload{ if (!interface_exists(UnloadableInterface::class)) { interface UnloadableInterface { public function unregister(); } } }//ns Frdlweb\Contract\Autoload namespace Frdlweb\Contract\Autoload{ if (!interface_exists(StackOrderAwareLoaderInterface::class)) { interface StackOrderAwareLoaderInterface { public const HAS_ALWAYS_CORRECT_STACK_ORDER = true; public function register(bool $prepend = false); } } }//ns Frdlweb\Contract\Autoload namespace frdl\implementation\psr4{ use Frdlweb\Contract\Autoload\CodebaseInterface as CodebaseInterface; class RemoteAutoloaderApiClient implements \Frdlweb\Contract\Autoload\LoaderInterface, \Frdlweb\Contract\Autoload\StackOrderAwareLoaderInterface, \Frdlweb\Contract\Autoload\UnloadableInterface, \Frdlweb\Contract\Autoload\ResolverInterface, \Frdlweb\Contract\Autoload\ClassLoaderInterface, \Frdlweb\Contract\Autoload\Psr4GeneratorInterface, \Frdlweb\Contract\Autoload\RemoteClassmapGeneratorInterface, \Frdlweb\Contract\Autoload\ClassmapGeneratorApiInterface, \Frdlweb\Contract\Autoload\AliasMapGeneratorInterface { public const HASH_ALGO = 'sha1'; public const ACCESS_LEVEL_SHARED = 0; public const ACCESS_LEVEL_PUBLIC = 1; public const ACCESS_LEVEL_OWNER = 2; public const ACCESS_LEVEL_PROJECT = 4; public const ACCESS_LEVEL_BUCKET = 8; public const ACCESS_LEVEL_CONTEXT = 16; public const CLASSMAP_DEFAULTS = [ //Concrete Classes: \frdlweb\Thread\ShutdownTasks::class => 'https://raw.githubusercontent.com/frdl/shutdown-helper/master/src/ShutdownTasks.php', // NAMESPACES = \\ at the end: 'frdl\\Proxy\\' => 'https://raw.githubusercontent.com/frdl/proxy/master/src/${class}.php?cache_bust=${salt}', // 'DI\\Definition\\' => 'https://raw.githubusercontent.com/PHP-DI/PHP-DI/6.0-release/src/Definition/${class}.php?cache_bust=${salt}', // ALIAS = @ as first char: '@Webfan\\Autoloader\\Remote' => __CLASS__, '@'.\Webfat\Keychain::class => \Webfan\KeychainOld::class, '@'.\frdl\Facades::class => \Webfan\FacadesManager::class,//\Statical\Manager::class, //Versions at Webfan: // Default/Fallback Versions Server: \webfan\hps\Format\DataUri::class => 'https://webfan.de/install/?salt=${salt}&source=${class}', // Stable/Current Versions Server: //\webfan\hps\Format\DataUri::class => 'https://webfan.de/install/stable/?salt=${salt}&source=webfan\hps\Format\DataUri', // Latest/Beta Versions Server: // \webfan\hps\Format\DataUri::class => 'https://webfan.de/install/latest/?salt=${salt}&source=webfan\hps\Format\DataUri', //Concrete Classes // \Webfan\cta\HashType\HashTypeInterface::class => 'https://raw.githubusercontent.com/frdl/cta/main/src/HashTypeInterface.php', // \Webfan\cta\HashType\XHashSha1::class => 'https://raw.githubusercontent.com/frdl/cta/main/src/XHashSha1.php', // \Webfan\cta\Storage\StorageInterface::class => 'https://raw.githubusercontent.com/frdl/cta/main/src/StorageInterface.php', //misc... //You can have functions autoloading 'GuzzleHttp\choose_handler' => 'https://webfan.de/install/?salt=${salt}&version=${version}&source=GuzzleHttp\choose_handler', \GuzzleHttp\LoadGuzzleFunctionsForFrdl::class => 'https://webfan.de/install/?salt=${salt}&version=${version}&source=GuzzleHttp\LoadGuzzleFunctionsForFrdl', \Wehowski\Gist\Http\Response\Helper::class => 'https://gist.githubusercontent.com/wehowski/d762cc34d5aa2b388f3ebbfe7c87d822/raw/5c3acdab92e9c149082caee3714f0cf6a7a9fe0b/Wehowski%255CGist%255CHttp%255CResponse%255CHelper.php?cache_bust=${salt}', ]; public static $alwaysAppendLoader = true; protected $salted = false; protected $selfDomain; protected $server; protected $domain; protected $version; protected $allowFromSelfOrigin = false; protected $prefixes = []; /** $afterMiddleware = [ "/(example\.com)/", function($code){ //.... return $code; } ]; $afterMiddleware = [ function($url){ //.... return true; //or false to en-/disable middleware }, function($code){ //.... return $code; / * validated/transformed code, invalidate with not string (e.g. bool or Excpetion) * / } ]; ->withAfterMiddleware($afterMiddleware[0], $afterMiddleware[1]) $beforeMiddleware = function($class, &$loader){ //.... return false; / * return false to skip this autoloader, return any/VOID to continue * / }; ->withBeforeMiddleware($beforeMiddleware) */ protected $afterMiddlewares = []; protected $beforeMiddlewares = []; protected $urlRewriterMiddlewares = []; protected $cacheDir; protected $cacheLimit = 0; protected static $instances = []; protected $alias = []; protected static $classmap = []; protected static $existsCache = []; protected $salt; protected $httTimeout = 90; public static $increaseTimelimit = true; protected $userAgent = null; protected $_TRANSPORTS = [ //'http', // 'tor', ]; protected $transport = 'http'; protected $_calledWIthDefaultMethods = []; protected $ApiManager = null;//CodebaseInterface public function setManager(CodebaseInterface $Manager) { $this->ApiManager = $Manager; } public function getManager(?bool $foce =false) : CodebaseInterface { if(null === $this->ApiManager && true===$foce){ } return $this->ApiManager; } protected function _forceManager() : CodebaseInterface|bool { if(\class_exists(\Webfan\FacadesManager::class)){ // return IO4:: // $this->ApiManager } return $this->ApiManager; } public function withTransport(string $schema, array | \callable | \Closure $handler){ $this->_TRANSPORTS[$schema] = $handler; } public function withTimeout(int $timeout){ $this->httTimeout = $timeout; } public function setTransport(string $transport){ if(!isset($this->_TRANSPORTS[$transport])){ throw new \Exception(sprintf('%sTransport is not set in %s. Use withTransport to define a callable for it!', ucfirst($transport), __METHOD__)); } $this->transport = $transport; } public function withUserAgent(string $userAgent){ $this->userAgent = $userAgent; } //$urlRewriterMiddlewares public function withUrlRewriterMiddleware( \callable | \closure $Middleware){ $this->urlRewriterMiddlewares[]= $Middleware; return $this; } public function withBeforeMiddleware( \callable | \closure $Middleware){ $this->beforeMiddlewares[]= $Middleware; return $this; } public function withAfterMiddleware(\callable | \closure | string $condition, \callable | \closure $filter){ $this->afterMiddlewares[]= [$condition, $filter]; return $this; } public function addNamespace($prefix, $resourceOrLocation, $prepend = false){ return $this->withNamespace($prefix, $resourceOrLocation, $prepend); } public function withNamespace($prefix, $server, $prepend = false) { $this->_withNamespace($prefix, $server, $prepend, true); } protected function _withNamespace($prefix, $server, $prepend = false, bool $sort = true) { $prefix = trim($prefix, '\\') . '\\'; // normalize the base directory with a trailing separator // $base_dir = rtrim($base_dir, DIRECTORY_SEPARATOR) . '/'; // initialize the namespace prefix array if (isset($this->prefixes[$prefix]) === false) { $this->prefixes[$prefix] = []; } // retain the base directory for the namespace prefix if ($prepend) { array_unshift($this->prefixes[$prefix], $server); } else { array_push($this->prefixes[$prefix], $server); } if($sort){ krsort($this->prefixes); } } public function withClassmap(?array $classMap = null) { krsort(self::$classmap); if(null !== $classMap){ foreach($classMap as $class => $server){ if('@' === substr($class, 0, 1) && is_string($server)){ $this->_withAlias($class, $server, false); }elseif('\\' === substr($class, -1)){ $this->_withNamespace($class, $server, false, false); }else{ self::$classmap[$class] = $server; } } } krsort($this->alias); krsort($this->prefixes); return self::$classmap; } public function withAlias(string $alias, string $rewrite) { $this->_withAlias($alias, $rewrite, true); } protected function _withAlias(string $alias, string $rewrite, bool $sort = true) { $this->alias[ltrim($alias, '@')] = $rewrite; if($sort){ krsort($this->alias); } } public function withSalt(bool $salted = null) { if(null !== $salted){ $this->salted = $salted; } return $this->salted; } //ClassmapGeneratorApiInterface public function getClassmapCachefileFor(string $app, string $version, string $phpVersion = \PHP_VERSION) : string { $dir= (is_string($this->cacheDir)) ? rtrim($this->cacheDir, \DIRECTORY_SEPARATOR.' \\/ ').\DIRECTORY_SEPARATOR : \sys_get_temp_dir().\DIRECTORY_SEPARATOR; $dir.= '~application-classmaps-caches'. \DIRECTORY_SEPARATOR; $dir.= 'remote-mapping-' . \substr(sha1($app), 0, 4). \DIRECTORY_SEPARATOR; $dir.= preg_replace("/[^A-Za-z0-9\-\.\_]/", '-', $app).\DIRECTORY_SEPARATOR; $dir.= sha1($version).strlen($version) . \DIRECTORY_SEPARATOR; $dir.= 'php-'.$phpVersion. \DIRECTORY_SEPARATOR; return $dir.'remote-classmap.php'; } protected function _isClassmapfileCacheExpired($file, $cache) : bool { switch($cache){ case false : return true; break; case true : return !file_exists($file); break; case \is_int($cache) : return !file_exists($file) || ($cache > 0 && filemtime($file) < time() - $cache); break; default : return !file_exists($file); break; } } //ClassmapGeneratorApiInterface // $cache = true : load from cache if exists // $cache = int : load from cache if it is newer than $cache seconds // $cache = false : invalidate cache for the classmap and load from implementation/API public function getClassmapFor(string $app, string $version, string $phpVersion = \PHP_VERSION, int | bool $cache = true) : array | bool { $file = $this->getClassmapCachefileFor($app, $version, $phpVersion); if(!$this->_isClassmapfileCacheExpired($file, $cache)){ return require $file; } $userAgent = 'Webfan/Fusio-Plugin-0.0.1' .' Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML,like Gecko) Chrome/107.0.0.0 Safari/537.36'; $this->withUserAgent($userAgent); $url = sprintf('https://api.webfan.de/v1/install/generate/%s/%s/autoloading/remote-mapping/%s/classmap', $app, $version, $phpVersion); $httpResult = $this->transport($url, 'GET', [ ], [ // 'ignore_errors' => false, 'timeout' =>$this->httTimeout * 3, ]); if($httpResult->status != '200'){ return false; } $json = $httpResult->body; $classMap = \json_decode($json); $classMap = (array)$classMap; $classMap =!isset($classMap['result']) ? $classMap : $classMap['result']; $classMap = (array)$classMap; $exp = \var_export($classMap, true); $phpCode = <<<PHPCODE <?php return $exp; PHPCODE; if(!is_dir(dirname($file))){ \mkdir(dirname($file), 0775, true); } \file_put_contents($file, $phpCode); return $classMap; } //ClassmapGeneratorApiInterface public function withClassmapFor(string $app, string $version, string $phpVersion = \PHP_VERSION, int | bool $cache = true) : bool { $classMapInfo = $this->getClassmapFor($app, $version, $phpVersion, $cache); if(is_bool($classMapInfo)){ return $classMapInfo; }elseif(is_array($classMapInfo)){ $classMap =!isset($classMapInfo['result']) ? $classMapInfo : $classMapInfo['result']; $classMap =(array)$classMap; $this->withClassmap($classMap); }else{ return false; } return true; } public function withDefaultValidators(string $dir = null,bool $increaseTimelimit = null){ if(\in_array(__METHOD__, $this->_calledWIthDefaultMethods)){ return; } $this->_calledWIthDefaultMethods[] = __METHOD__; if(null===$dir){ $dir= (is_dir($this->cacheDir)) ? $this->cacheDir : \sys_get_temp_dir().\DIRECTORY_SEPARATOR; } $httTimeout = $this->httTimeout; $publicKeyChanged = false; $increaseTimelimit = is_null($increaseTimelimit) ? self::$increaseTimelimit : $increaseTimelimit; $me = &$this; $setPublicKey = function($baseUrl, $expFile, $pubKeyFile) use($httTimeout, $increaseTimelimit, $me) { if(file_exists($expFile)){ $expires = intval(file_get_contents($expFile)); }else{ $expires = 0; } if(!is_dir(dirname($expFile))){ mkdir(dirname($expFile), 0755, true); } if(!is_dir(dirname($pubKeyFile))){ mkdir(dirname($pubKeyFile), 0755, true); } if($expires > 0 && ($expires === time() || ($expires > time() - 3 && $expires < time() + 3))){ sleep(3); } if($expires <= time() || !file_exists($pubKeyFile) ){ if($increaseTimelimit){ set_time_limit(max(max($httTimeout,240), intval(ini_get('max_execution_time')) + max($httTimeout,240))); } $httpResult = $me->transport($baseUrl.'source='.urlencode('@server.key'), 'GET', [ ], [ // 'ignore_errors' => false, 'timeout' =>$this->httTimeout * 4, ]); $key = $httpResult->body; if(false === $key || empty($key) ){ throw new \Exception('Cannot get '. $baseUrl.'source=@server.key in '.__METHOD__); return; } foreach($httpResult->headers as $i => $header){ $h = explode(':', $header); if('x-frdlweb-source-expires' === strtolower(trim($h[0]))){ file_put_contents($expFile, trim($h[1]) ); break; } } file_put_contents($pubKeyFile, $key); }//keyfile expired and refetched }; $getDefaultValidatorForUrl = function($baseUrl, $cacheDir, $increaseTimelimit = true) use($httTimeout, $setPublicKey, &$publicKeyChanged) { $parsedUrl = parse_url($baseUrl); $host = is_array($parsedUrl) && isset($parsedUrl['host']) ? $parsedUrl['host'] : 'DNS_PROBE_FINISHED_NXDOMAIN'; $expFile = rtrim($cacheDir, '\\/ ') . \DIRECTORY_SEPARATOR.'validator-'.$host.'-'.sha1($baseUrl).strlen($baseUrl).'.expires.txt'; $pubKeyFile = rtrim($cacheDir, '\\/ ') . \DIRECTORY_SEPARATOR.'validator-'.$host.'-'.sha1($baseUrl).strlen($baseUrl).'.public-key.txt'; $condition = function($url, &$loader, $class) use($httTimeout, $baseUrl, $increaseTimelimit){ if($baseUrl === substr($url, 0, strlen($baseUrl) ) && $class !== \PhpParser\PrettyPrinter\Standard::class ){ if($increaseTimelimit){ set_time_limit(max(max($httTimeout,120), intval(ini_get('max_execution_time')) + max($httTimeout,120))); } return true; }else{ return false; } }; $cb = null; $filter = function($code, &$loader, $class, $c = 0) use(&$cb, $baseUrl, $expFile, $pubKeyFile, $setPublicKey, &$publicKeyChanged) { $c++; $sep = 'X19oYWx0X2NvbXBpbGVyKCk7'; $my_signed_data=$code; $setPublicKey($baseUrl, $expFile, $pubKeyFile); if(!file_exists($pubKeyFile)){ return new \Exception("ERROR -- missing public key for ".$class." at ".$baseUrl.": ".htmlentities(substr($code, 0, 1024).'...')); } $public_key = file_get_contents($pubKeyFile); list($plain_data,$sigdata) = explode(base64_decode($sep), $my_signed_data, 2); if(empty($sigdata) || empty($plain_data)){ return new \Exception("ERROR -- unsigned data for ".$class." at ".$baseUrl.": ".htmlentities(substr($code, 0, 1024).'...')); } list($nullVoid,$old_sig_1) = explode("----SIGNATURE:----", $sigdata, 2); list($old_sig,$ATTACHMENT) = explode("----ATTACHMENT:----", $old_sig_1, 2); $old_sig = $old_sig ? base64_decode($old_sig) : ''; $ATTACHMENT = $ATTACHMENT ? base64_decode($ATTACHMENT) : ''; if(empty($old_sig)){ return new \Exception("ERROR -- unsigned data for ".$class." at ".$baseUrl.": ".htmlentities(substr($code, 0, 1024).'...')); } \openssl_public_decrypt($old_sig, $decrypted_sig, $public_key); $data_hash = sha1($plain_data.$ATTACHMENT).substr(str_pad(strlen($plain_data.$ATTACHMENT).'', 128, strlen($plain_data.$ATTACHMENT) % 10, \STR_PAD_LEFT), 0, 128); if($decrypted_sig === $data_hash && strlen($data_hash)>0){ return $plain_data; }else{ if(!$publicKeyChanged && $c <= 1){ $publicKeyChanged = true; unlink($pubKeyFile); unlink($expFile); $setPublicKey($baseUrl, $expFile, $pubKeyFile); return $cb($code, $loader, $class, $c); } return new \Exception("ERROR -- untrusted signature"); } }; $cb = $filter; return [$condition, $filter]; }; $getDefaultValidators = function($cacheDir,bool $increaseTimelimit = null) use($getDefaultValidatorForUrl) { return [ $getDefaultValidatorForUrl('https://latest.software-download.frdlweb.de/?', $cacheDir, $increaseTimelimit), $getDefaultValidatorForUrl('https://stable.software-download.frdlweb.de/?', $cacheDir, $increaseTimelimit), $getDefaultValidatorForUrl('https://startdir.de/install/latest/?', $cacheDir, $increaseTimelimit), $getDefaultValidatorForUrl('https://startdir.de/install/stable/?', $cacheDir, $increaseTimelimit), $getDefaultValidatorForUrl('https://startdir.de/install/?', $cacheDir, $increaseTimelimit), $getDefaultValidatorForUrl('https://webfan.de/install/stable/?', $cacheDir, $increaseTimelimit), $getDefaultValidatorForUrl('https://webfan.de/install/latest/?', $cacheDir, $increaseTimelimit), $getDefaultValidatorForUrl('https://webfan.de/install/?', $cacheDir, $increaseTimelimit), ]; }; foreach($getDefaultValidators($dir, $increaseTimelimit) as $validator){ $this->withAfterMiddleware($validator[0], $validator[1]); } return $this; } public function file_get_contents($url){ $transport = $this->transport($url); return $transport->body; } public function withWebfanWebfatDefaultSettings(string $dir = null,bool $increaseTimelimit = null){ if(\in_array(__METHOD__, $this->_calledWIthDefaultMethods)){ return; } $this->_calledWIthDefaultMethods[] = __METHOD__; if(null===$dir){ $dir= (is_dir($this->cacheDir)) ? $this->cacheDir : \sys_get_temp_dir().\DIRECTORY_SEPARATOR; } /* some dirty workaround patches... */ $this->withBeforeMiddleware(function($class, &$loader){ $dir = dirname($loader->file('Foo')); switch($class){ case 'Sabre\\' === substr($class, 0, strlen('Sabre\\') ) : $aDir = dirname($dir).\DIRECTORY_SEPARATOR.'autoload-files-conditional'.\DIRECTORY_SEPARATOR.'sabre-dav'; if(!is_dir($aDir)){ mkdir($aDir, 0775, true); } $aFile = $aDir.\DIRECTORY_SEPARATOR.'functions-resolve.php'; if(!file_exists($aFile)){ file_put_contents($aFile, base64_decode($loader->file_get_contents( 'https://latest.software-download.frdlweb.de/?source=Sabre\Uri\resolve&salt='.time()))); } if (!in_array($aFile, get_included_files())) { require_once $aFile; } return true; break; case \Embed\OEmbed::class : $classFile = $loader->file($class); $dirName = dirname($classFile).\DIRECTORY_SEPARATOR.'resources'; if(!is_dir($dirName)){ mkdir($dirName, 0775, true); } $aFile = $dirName.\DIRECTORY_SEPARATOR.'oembed.php'; if(!file_exists($aFile)){ file_put_contents($aFile, $loader->file_get_contents('https://raw.githubusercontent.com/oscarotero/Embed/530593af5ef7c6701a075eee1be58aa5aaa17c95/src/resources/oembed.php?cache_bust='.time())); } $aFile = $dirName.\DIRECTORY_SEPARATOR.'suffix.php'; if(!file_exists($aFile)){ file_put_contents($aFile, $loader->file_get_contents('https://raw.githubusercontent.com/oscarotero/Embed/530593af5ef7c6701a075eee1be58aa5aaa17c95/src/resources/suffix.php?cache_bust='.time())); } return true; break; case \ActivityPhp\Version::class : $classFile = $loader->file($class); $dirName = dirname(dirname(dirname($classFile))); $jsonFile = $dirName.\DIRECTORY_SEPARATOR.'composer.json'; if(!file_exists($jsonFile)){ $theJson = $loader->file_get_contents('https://raw.githubusercontent.com/landrok/activitypub/f30b8f726cf1a196337ec065536eba2d66a4b329/composer.json'); file_put_contents($jsonFile, $theJson); } break; case \Smarty::class : $aDir = dirname($dir).\DIRECTORY_SEPARATOR.'autoload-files-conditional'.\DIRECTORY_SEPARATOR.'smarty-php'; if(!is_dir($aDir)){ mkdir($aDir, 0775, true); } $aFile = $aDir.\DIRECTORY_SEPARATOR.'functions.php'; if(!file_exists($aFile)){ file_put_contents($aFile, $loader->file_get_contents('https://raw.githubusercontent.com/smarty-php/smarty/v4.3.0/libs/functions.php?cache_bust='.time())); } if (!in_array($aFile, get_included_files())) { require_once $aFile; } return true; break; case \DI\Compiler\Compiler::class : $aDir = dirname($loader->file($class)); if(!is_dir($aDir)){ mkdir($aDir, 0755, true); } $aFile = $aDir.\DIRECTORY_SEPARATOR.'Template.php'; if(!file_exists($aFile)){ file_put_contents($aFile, $loader->file_get_contents('https://raw.githubusercontent.com/PHP-DI/PHP-DI/6d4ac8be4b0322200a55a0fbf5d32b2be3c1062b/src/Compiler/Template.php?cache_bust='.time())); } return true; break; case \Webfan\Webfat\App\ContainerAppKernel::class : case \DI\ContainerBuilder::class : case 'DI\\' === substr($class,0 , 3) : $aDir = dirname($dir).\DIRECTORY_SEPARATOR.'autoload-files-conditional'.\DIRECTORY_SEPARATOR.'php-di'; if(!is_dir($aDir)){ mkdir($aDir, 0775, true); } $aFile = $aDir.\DIRECTORY_SEPARATOR.'functions.php'; if(!file_exists($aFile)){ file_put_contents($aFile, $loader->file_get_contents('https://raw.githubusercontent.com/PHP-DI/PHP-DI/6.0-release/src/functions.php?cache_bust='.time())); } if (!in_array($aFile, get_included_files())) { require_once $aFile; } return true; break; case \Amp\Dns\Resolver::class : case 'Amp\Dns\\' === substr($class, 0, strlen('Amp\Dns\\') ) : $aDir = dirname($dir).\DIRECTORY_SEPARATOR.'autoload-files-conditional'.\DIRECTORY_SEPARATOR.'amp-dns'; if(!is_dir($aDir)){ mkdir($aDir, 0775, true); } $aFile = $aDir.\DIRECTORY_SEPARATOR.'functions.php'; if(!file_exists($aFile)){ file_put_contents($aFile, $loader->file_get_contents('https://raw.githubusercontent.com/amphp/dns/v1.2.3/lib/functions.php?cache_bust='.time())); } if (!in_array($aFile, get_included_files())) { require_once $aFile; } return true; break; case \Amp\Loop::class : case 'Amp\\' === substr($class, 0, strlen('Amp\\') ) && 'Amp\Dns\\' !== substr($class, 0, strlen('Amp\Dns\\') ) : foreach(['functions.php', 'Internal/functions.php'] as $file){ $aDir = dirname($dir).\DIRECTORY_SEPARATOR.'autoload-files-conditional'.\DIRECTORY_SEPARATOR.'amp-amp' .\DIRECTORY_SEPARATOR. dirname(str_replace('/', \DIRECTORY_SEPARATOR, $file)); if(!is_dir($aDir)){ mkdir($aDir, 0775, true); } $aFile = $aDir.\DIRECTORY_SEPARATOR.'functions.php'; if(!file_exists($aFile)){ file_put_contents($aFile, $loader->file_get_contents('https://raw.githubusercontent.com/amphp/amp/v2.6.2/lib/'.$file.'?cache_bust='.time())); } if (!in_array($aFile, get_included_files())) { require_once $aFile; } } return true; break; case \Webfan\Webfat\EventModule::class : case \Webfan\Webfat\App\Router::class : case \Webfan\Router::class : case 'Opis\Closure\\' === substr($class, 0, strlen('Opis\Closure\\') ) : $aDir = dirname($dir).\DIRECTORY_SEPARATOR.'autoload-files-conditional'.\DIRECTORY_SEPARATOR.'opis-closure'; if(!is_dir($aDir)){ mkdir($aDir, 0775, true); } $aFile = $aDir.\DIRECTORY_SEPARATOR.'functions.php'; if(!file_exists($aFile)){ //file_put_contents($aFile, file_get_contents('https://raw.githubusercontent.com/opis/closure/3.6.3/functions.php?cache_bust='.time())); file_put_contents($aFile, $loader->file_get_contents('https://raw.githubusercontent.com/opis/closure/3.5.5/functions.php?cache_bust='.time())); } if (!in_array($aFile, get_included_files())) { require_once $aFile; } return true; break; case 'Spatie\\Once\\' === substr($class, 0, strlen('Spatie\\Once\\') ) : $aDir = dirname($dir).\DIRECTORY_SEPARATOR.'autoload-files-conditional'.\DIRECTORY_SEPARATOR.'spatie-once'; if(!is_dir($aDir)){ mkdir($aDir, 0775, true); } $aFile = $aDir.\DIRECTORY_SEPARATOR.'functions.php'; if(!file_exists($aFile)){ file_put_contents($aFile, $loader->file_get_contents('https://raw.githubusercontent.com/spatie/once/3.1.0/src/functions.php?cache_bust='.time())); } if (!in_array($aFile, get_included_files())) { require_once $aFile; } return true; break; case 'Guzzle' === substr($class, 0, strlen('Guzzle') ) : $aDir = dirname($dir).\DIRECTORY_SEPARATOR.'autoload-files-conditional'.\DIRECTORY_SEPARATOR.'guzzle-http'; if(!is_dir($aDir)){ mkdir($aDir, 0775, true); } $aFile = $aDir.\DIRECTORY_SEPARATOR.'functions.php'; $aFile_2 = $aDir.\DIRECTORY_SEPARATOR.'functions_2.php'; $aFile_3 = $aDir.\DIRECTORY_SEPARATOR.'functions_3.php'; if(!file_exists($aFile)){ file_put_contents($aFile, base64_decode($loader->file_get_contents( 'https://webfan.de/install/?source=GuzzleHttp\Psr7\stream_for&salt='.time()) )); } if(!file_exists($aFile_2)){ file_put_contents($aFile_2, base64_decode( $loader->file_get_contents( 'https://webfan.de/install/?source=GuzzleHttp\choose_handler&salt='.time()) ) ); } if(!file_exists($aFile_3)){ file_put_contents($aFile_3, $loader->file_get_contents( 'https://raw.githubusercontent.com/guzzle/promises/b94b2807d85443f9719887892882d0329d1e2598/src/functions.php') ); } if (!in_array($aFile, \get_included_files())) { require_once $aFile; } if (!in_array($aFile_2, \get_included_files())) { require_once $aFile_2; } if (!in_array($aFile_3, \get_included_files())) { require_once $aFile_3; } return true; break; default: return true; break; } /* return true; return false to skip this autoloader, return any/VOID to continue */ }); $this->withClassmapFor('default', 'latest', \PHP_VERSION, $this->cacheLimit); /* $this->withUrlRewriterMiddleware(function($url){ $now = new \DateTimeImmutable(); if( intval($now->format('Y')) <= 2023 ){ $p = \parse_url($url); if('webfan.de' === $p['host']){ $p['host'] = 'startdir.de'; $url = \frdl\implementation\psr4\RemoteAutoloaderApiClient::unparse_url( $url, $p, false ); } } return $url; }); */ return $this; } //end default-patches public static function getInstance( $server = 'https://webfan.de/install/stable/?source={class}&salt={salt}', $register = true, $version = 'latest', $allowFromSelfOrigin = true, $salted = false, $classMap = null, $cacheDirOrAccessLevel = self::ACCESS_LEVEL_SHARED, $cacheLimit = null, $password = null ) { $key = static::ik(); if(is_array($server)){ // $arr = []; foreach($server as $s){ // $arr[]= self::getInstance($s['server'], $s['register'], $s['version'], $s['allowFromSelfOrigin'], $s['salted'], $s['classmap'], $s['cacheDirOrAccessLevel'], $s['cacheLimit'], $s['password']); } // if(2 > count(func_get_args()) ){ // return self::$instances[count(self::$instances)-1]; // } $server = 'file://'.getcwd().\DIRECTORY_SEPARATOR; ///$key = sha1(getcwd()).'.localhost'; }//elseif(is_callable($server)){ // $key = \spl_object_id($server); // }elseif(is_string($server)){ // $key = $server; // } if(!isset(self::$instances[static::ik()])){ // self::$instances[$key] = new self($server, $register, $version, $allowFromSelfOrigin, $salted, $classMap, $cacheDirOrAccessLevel, $cacheLimit, $password); } return self::$instances[static::ik()]; } public static function ik() { return \getmypid(); } public function __construct( $server = 'https://webfan.de/install/stable/?source={{class}}&salt={{salt}}', $register = true, $version = 'latest', $allowFromSelfOrigin = true, $salted = false, $classMap = null, $cacheDirOrAccessLevel = self::ACCESS_LEVEL_SHARED, $cacheLimit = null, $password = null ) { $this->withTransport('http', [$this, 'fetchHttp']); $this->setTransport('http'); $this->salt = mt_rand(10000000,99999999); $key = static::ik(); self::$instances[static::ik()] = &$this; $defauoltcacheLimit = -1; $bucketHash = $this->generateHash([ self::class//, // $version ], '', self::HASH_ALGO, '-'); switch($cacheDirOrAccessLevel){ case self::ACCESS_LEVEL_PUBLIC : $bucket = \get_current_user().\DIRECTORY_SEPARATOR.'shared'; break; case self::ACCESS_LEVEL_OWNER : $bucket = \get_current_user().\DIRECTORY_SEPARATOR .$this->generateHash([ $bucketHash, $version, \get_current_user ( ), //$_SERVER['SERVER_NAME'] // $_SERVER['SERVER_ADDR'] ], $password, self::HASH_ALGO, '-'); break; case self::ACCESS_LEVEL_PROJECT : $bucket = \get_current_user ( ).\DIRECTORY_SEPARATOR .$this->generateHash([ $bucketHash, $version, \get_current_user ( ), $_SERVER['SERVER_NAME'], $_SERVER['SERVER_ADDR'], basename(getcwd()), realpath(getcwd()), ], $password, self::HASH_ALGO, '-'); break; case self::ACCESS_LEVEL_BUCKET : $bucket = \get_current_user ( ).\DIRECTORY_SEPARATOR .$this->generateHash([ $bucketHash, $version ], $password, self::HASH_ALGO, '-'); break; case self::ACCESS_LEVEL_CONTEXT : $bucket = \get_current_user ( ).\DIRECTORY_SEPARATOR .$this->generateHash([ $bucketHash, $version, \get_current_user ( ), $_SERVER['SERVER_NAME'], $_SERVER['SERVER_ADDR'], $_SERVER['REMOTE_ADDR'], basename(getcwd()), realpath(getcwd()) ], $password, self::HASH_ALGO, '-'); break; case self::ACCESS_LEVEL_SHARED : default: $bucket = '_'.\DIRECTORY_SEPARATOR.'shared'; $defauoltcacheLimit = $defauoltcacheLimit > 0 ? \max($defauoltcacheLimit, 5 * 60) : 24 * 60 * 60; break; } $this->cacheLimit = (is_int($cacheLimit)) ? $cacheLimit : ((isset($_ENV['FRDL_HPS_PSR4_CACHE_LIMIT']))? $_ENV['FRDL_HPS_PSR4_CACHE_LIMIT'] : $defauoltcacheLimit); $this->cacheDir =/* (is_string($cacheDirOrAccessLevel) && is_dir($cacheDirOrAccessLevel) && is_readable($cacheDirOrAccessLevel) && is_writeable($cacheDirOrAccessLevel) ) */ is_string($cacheDirOrAccessLevel) ? $cacheDirOrAccessLevel : \sys_get_temp_dir().\DIRECTORY_SEPARATOR // .'.frdl'.\DIRECTORY_SEPARATOR .$bucket.\DIRECTORY_SEPARATOR .'lib'.\DIRECTORY_SEPARATOR .'php'.\DIRECTORY_SEPARATOR .'src'.\DIRECTORY_SEPARATOR .'psr4'.\DIRECTORY_SEPARATOR; /* */ $valCacheDir; $valCacheDir = (function($CacheDir, $checkAccessable = true, $checkNotIsSysTemp = true, $r = null) use(&$valCacheDir){ if(null ===$r)$r=dirname($CacheDir); $checkRoot = substr($r, 0, strlen($CacheDir) ); $checkSame = $r === $CacheDir; $checked = false === $checkNotIsSysTemp || false === $checkSame || ( ( rtrim($CacheDir, \DIRECTORY_SEPARATOR.'/\\ ') !== rtrim(\sys_get_temp_dir(),\DIRECTORY_SEPARATOR.'/\\ ') && 'tmp' !== basename($CacheDir) // && 'tmp' !== basename(dirname($CacheDir)) ) ); return ( $checkAccessable === false || ( is_dir($CacheDir) && is_writable($CacheDir) && is_readable($CacheDir) ) ) && true === $checked ? true : false ; }); if(!is_dir($this->cacheDir)){ mkdir($this->cacheDir, 0775,true); }else{ chmod($this->cacheDir, 0775); } if(!$valCacheDir($this->cacheDir,false,false) ){ throw new \Exception('Bootstrap error in '.basename(__FILE__).' '.__LINE__.' for '.$this->cacheDir); } if(!is_array($classMap)){ $classMap = self::CLASSMAP_DEFAULTS; } $this->withSalt($salted); $this->withClassmap($classMap); $this->allowFromSelfOrigin = $allowFromSelfOrigin; $this->version=$version; $this->server = $server; $_self = (isset($_SERVER['SERVER_NAME'])) ? $_SERVER['SERVER_NAME'] : ( (isset($_SERVER['HTTP_HOST']) && \php_sapi_name() !== 'cli' ) ? $_SERVER['HTTP_HOST'] : \php_uname("n")); $h = explode('.', $_self); $dns = array_reverse($h); $this->selfDomain = $dns[1].'.'.$dns[0]; if(is_string($this->server)){ $h = explode('.', $this->server); $dns = array_reverse($h); $this->domain = $dns[1].'.'.$dns[0]; } if(true === $register && !$this->allowFromSelfOrigin && $this->domain === $this->selfDomain){ $message = 'Autoloading from remote at this domain at self local server is disabled in '.__METHOD__; throw new \Exception($message); //$register = false; } $this->withDefaultValidators($this->cacheDir,null); if(true === $register){ $this->register(); } } public function generateHash(array $chunks = [], $key = null, $algo = 'sha1', $delimiter = '-', &$ctx = null) { $size = count($chunks); $initial = null === $ctx; $asString = serialize($chunks);//implode($delimiter, $chunks); $buffer = ''; $l = 0; $c = $size + ($initial); if(null===$key || empty($key)){ $c++; $key = \hash( $algo , serialize([$delimiter, [$algo,$size, $delimiter, $c, $asString]]) ); } $MetaCtx = \hash_init ( $algo , \HASH_HMAC, $key ) ; \hash_update($MetaCtx, $key); if( true === $initial || null === $ctx){ $ctx = \hash_init ( $algo , \HASH_HMAC, $key ) ; } while(count($chunks) > 0 && $data = array_shift($chunks)){ $buffer .=$data; $l += strlen($data); $c += count($chunks); \hash_update($ctx, $data); \hash_update($MetaCtx, $buffer); \hash_update($MetaCtx, $l); \hash_update($MetaCtx, $c); } $c++; //$h2 = $this->generateHash([$algo,count($chunks),$l,$asString, $delimiter, $key, $c], $key, 'sha1', $delimiter, $ctx); $h2 = \hash( $algo , $size .'.'. $l. '.' . $c. '.' . strlen($buffer.$asString) ); \hash_update($ctx, $h2); \hash_update($MetaCtx, $h2); $c++; $h3 = hash_final($MetaCtx); \hash_update($ctx, $h3); $hash = hash_final($ctx); return implode($delimiter, [$size .'.'. $l. '.' . $c. '.' . (strlen($h2.$buffer.$asString) * $c) % 20, $h3, $hash, $h2]); } public function str_contains($haystack, $needle, $ignoreCase = false) { if ($ignoreCase) { $haystack = strtolower($haystack); $needle = strtolower($needle); } $needlePos = strpos($haystack, $needle); // return ($needlePos === false ? false : ($needlePos+1)); return $needlePos === false ? false : true; } public function str_parse_vars($string, $start = '[', $end = '/]', $variableDelimiter = '=') { preg_match_all('/' . preg_quote($start, '/') . '(.*?)'. preg_quote($end, '/').'/i', $string, $m); $out = []; foreach($m[1] as $key => $value){ $type = explode($variableDelimiter,$value); if(sizeof($type)>1){ if(!is_array($out[$type[0]])) $out[$type[0]] = []; $out[$type[0]][] = $type[1]; } else { $out[] = $value; } } return $out; } public function getUrl($class, $server, $salt = null, $parseVars = false) { if(!is_string($salt))$salt=$this->salt; $url = false; if(is_string($server) ){ if(substr($server, 0, strlen('http://')) === 'http://' || substr($server, 0, strlen('https://')) === 'https://'){ // $url = str_replace(['${salt}', '${class}', '${version}'], [$salt, $class, $this->version], $server); $url = $server; }elseif('~' === substr($server, 0, 1) || is_string($server) && '.' === substr($server, 0, 1) || substr($server, 0, strlen('file://')) === 'file://'){ $url = \DIRECTORY_SEPARATOR.str_replace('\\', \DIRECTORY_SEPARATOR, getcwd() .str_replace(['file://', '~'/*, '${salt}', '${class}', '${version}'*/], ['', (!empty(getenv('FRDL_HOME'))) ? getenv('FRDL_HOME') : getenv('HOME')/*, $salt, $class, $this->version*/], $server). '.php'); }/*elseif(preg_match("/^([a-z0-9]+)\.webfan\.de$/", $server, $m) && false !== strpos($server, '.') ){ $url = 'https://'.$m[1].'.webfan.de/install/?salt=${salt}&source=${class}&version=${version}'; }*/elseif(preg_match("/^([\w\.^\/]+)(\/[.*]+)?$/", $server, $m) && false !== strpos($server, '.') ){ $url = 'https://'.$m[1].((isset($m[2])) ? $m[2] : '/'); }//else{ if(!$this->str_contains($url, '${class}', false) && '.php' !== substr(explode('?', $url)[0], -4)){ $url = rtrim($url, '/').'/${class}'; } if(!$this->str_contains($url, '${salt}', false)){ $url .= (( $this->str_contains($url, '?', false) ) ? '&' : '?').'salt=${salt}'; } }elseif(is_callable($server)){ $url = call_user_func_array($server, [$class, $this->version, $salt]); }elseif(is_object($server) && is_callable([$server, 'has']) && is_callable([$server, 'get']) && true === call_user_func_array([$server, 'has'], [$class]) ){ $url = call_user_func_array([$server, 'get'], [$class, $this->version, $salt]); }elseif(is_object($server) && is_callable([$server, 'get']) ){ $url = call_user_func_array([$server, 'get'], [$class, $this->version, $salt]); } return (true === $parseVars && is_string($url)) ? $this->replaceUrlVars($url, $salt, $class, $this->version) : $url; } public function replaceUrlVars($url, $salt, $class, $version) { if(empty($salt)){ $salt = $this->salt; } $url = preg_replace('/(\$\{class\})/',str_replace('\\', '/', $class), $url); $url = preg_replace('/(\$\{salt\})/', $salt, $url); $url = preg_replace( '/(\$\{version\})/',$version, $url); return $url; } /** * Loads the class file for a given class name. * * @param string $class The fully-qualified class name. * @return mixed The mapped file name on success, or boolean false on * failure. */ public function loadClass($class, $salt = null) { if(!is_string($salt)){ $salt = $this->salt; } $prefix = $class; // work backwards through the namespace names of the fully-qualified // class name to find a mapped file name while (false !== $pos = strrpos($prefix, '\\')) { // retain the trailing namespace separator in the prefix $prefix = substr($class, 0, $pos + 1); // the rest is the relative class name $relative_class = substr($class, $pos + 1); // try to load a mapped file for the prefix and relative class $mapped_file = $this->loadMappedSource($prefix, $relative_class, $salt); if ($mapped_file && $this->exists($mapped_file) ) { return $mapped_file; } // remove the trailing namespace separator for the next iteration // of strrpos() $prefix = rtrim($prefix, '\\'); } // never found a mapped file return $this->loadMappedSource('', $class, $salt); } /** * Load the mapped file for a namespace prefix and relative class. * * @param string $prefix The namespace prefix. * @param string $relative_class The relative class name. * @return mixed Boolean false if no mapped file can be loaded, or the * name of the mapped file that was loaded. */ protected function loadMappedSource($prefix, $relative_class, $salt = null) { if(!is_string($salt)){ $salt = $this->salt; } $url = false; $class = $prefix.$relative_class; //if(isset($this->alias[$class]) ){ // \webfan\hps\Format\DataUri // die(__LINE__.$class.' Alias: '.$this->alias[$class]); // } $pfx = !isset($this->alias[$prefix]) && substr($prefix,-1) === '\\' ? substr($prefix, 0, -1) : $prefix; if(isset($this->alias[$pfx]) ){ // \webfan\hps\Format\DataUri $originalClass = substr($this->alias[$pfx],-1) === '\\' ? substr($this->alias[$pfx], 0, -1) : $this->alias[$pfx]; $originalClass .= '\\'.$relative_class; $alias = $class; // die($classOrInterfaceExists.' <br />'.$alias.' <br />rc: '.$originalClass.'<br />'.$datUri); $classOrInterfaceExistsAndNotEqualsAlias =( class_exists($originalClass, $originalClass !== $alias) || interface_exists($originalClass, $originalClass !== $alias) || (function_exists('trait_exists') && trait_exists($originalClass, $originalClass !== $alias)) ) && $originalClass !== $alias; if($classOrInterfaceExistsAndNotEqualsAlias){ \class_alias($originalClass, $alias); } return true; //return $classOrInterfaceExistsAndNotEqualsAlias; } if(isset(self::$classmap[$class]) && is_string(self::$classmap[$class]) && '\\' !== substr($class, -1) && '\\' !== substr(self::$classmap[$class], -1) ){ $url = $this->getUrl($class, self::$classmap[$class], $salt, true); $url = $this->replaceUrlVars($url, $salt, $class, $this->version); if(is_string($url) && $this->exists($url) ){ return $url; } } if (isset($this->prefixes[$prefix]) ) { // look through base directories for this namespace prefix foreach ($this->prefixes[$prefix] as $server) { $url = $this->getUrl($relative_class, $server, $salt, true); $url = $this->replaceUrlVars($url, $salt, $relative_class, $this->version); if(is_string($url) && $this->exists($url) ){ return $url; } } } // never found it return $this->getUrl($class, $this->server, $salt, true); } /** * If a file exists, require it from the file system. * * @param string $file The file to require. * @return bool True if the file exists, false if not. */ protected function requireFile($file) { if (file_exists($file)) { try{ require $file; }catch(\Exception $e){ trigger_error($e->getMessage(), \E_USER_ERROR); return false; } return true; } return false; } public static function __callStatic($name, $arguments) { $me = (count(self::$instances)) ? self::$instances[0] : self::getInstance(); return call_user_func_array([$me, $name], $arguments); } public function __call($name, $arguments) { if(!in_array($name, ['fetch', 'fetchCode', '__invoke', 'register', 'getLoader', 'Autoload'])){ throw new \Exception('Method '.$name.' not allowed in '.__METHOD__); } return call_user_func_array([$this, $name], $arguments); } protected function fetch() { return call_user_func_array([$this, 'fetchCode'], func_get_args()); } public function exists($source) { if(isset(self::$existsCache[$source])){ return self::$existsCache[$source]; } if('http://'!==substr($source, 0, strlen('http://')) && 'https://'!==substr($source, 0, strlen('https://')) ){ $exists = is_file($source) && file_exists($source) && is_readable($source); if(true === $exists){ self::$existsCache[$source] = $exists; } return $exists; } $httpResult = $this->transport($source, 'HEAD', null, [ 'ignore_errors' => false, 'timeout' => max(1, floor($this->httTimeout / 2)), ]); $res = $httpResult->body; $exists = false !== $res; self::$existsCache[$source] = $exists; return $exists; } public function fetchHttp(string $url, string $method = 'GET', array $headers = null, array $options = null, array $httpOpts= null){ $httpOptions = [ 'http' => [ 'method' => $method, 'ignore_errors' => false, 'timeout' => $this->httTimeout, 'follow_location' => true, 'header'=> ""// "X-Source-Encoding: b64\r\n" // . "Content-Length: " . strlen($data) . "\r\n" , ] ]; if(is_array($httpOpts)){ $httpOptions = array_merge($httpOptions, $httpOpts); } if(is_array($options)){ $httpOptions['http'] = array_merge($httpOptions['http'], $options); } if(!is_array($headers)){ $headers = [ 'X-Source-Encoding' => 'b64', ]; } if(is_string($this->userAgent)){ $headers['User-Agent'] = $this->userAgent; } foreach($headers as $key => $value){ $httpOptions['http']['header'].= $key.": ". $value . "\r\n"; } $httpOptions['http']['method'] = $method; $transport = new \stdclass; $transport->context = stream_context_create($httpOptions); $transport->body = @file_get_contents($url, false, $transport->context); $transport->headers = array_merge([], isset($http_response_header) && is_array($http_response_header) ? $http_response_header : []); if(isset($transport->headers[0])){ preg_match('{HTTP\/\S*\s(\d{3})}', $transport->headers[0], $match); $transport->status = (isset($match[1])) ? $match[1] : (\is_string($transport->body) ? '200' : '404'); }else{ $transport->status = \is_string($transport->body) ? 200 : 404; // error_log('Wrong status code for '.$url.' in '.__METHOD__, \is_string($transport->body) ? \E_USER_NOTICE : \E_USER_WARNING); // error_log('Wrong status code for '.$url.' in '.__METHOD__, \E_USER_NOTICE); } return $transport; } /* by kibblewhite+php at live dot com https://www.php.net/manual/en/function.parse-url.php#125844 $test_url = 'http://usr:pss@example.com:81/mypath/myfile.html?a=b&b[]=2&b[]=3&z=9#myfragment'; $new_url_01_overwrite_query_params = self::unparse_url( $test_url, array( 'host' => 'new-hostname.tld', 'query' => array( 'test' => 'Hello World', 'a' => array( 'c', 'd' ), 'z' => 8 ), 'fragment' => 'new-fragment-value' ), false ); $new_url_02_mergewith_query_params = self::unparse_url( $test_url, array( 'query' => array( 'test' => 'Hello World', 'a' => array( 'c', 'd' ), 'z' => 8 ), 'fragment' => 'new-fragment-value' ), true ); */ public static function unparse_url( string $url, array $overwrite_parsed_url_array = null, bool $merge_query_parameters = null ) : string { $merge_query_parameters = is_bool($merge_query_parameters) ? $merge_query_parameters : true; $overwrite_parsed_url_array = is_array($overwrite_parsed_url_array) ? $overwrite_parsed_url_array : []; $parsed_url_array = \parse_url( $url ); $parsed_url_keys_array = [ 'scheme' => null, 'abempty' => isset( $parsed_url_array['scheme'] ) ? '://' : null, 'user' => null, 'authcolon' => isset( $parsed_url_array['pass'] ) ? ':' : null, 'pass' => null, 'authat' => isset( $parsed_url_array['user'] ) ? '@' : null, 'host' => null, 'portcolon' => isset( $parsed_url_array['port'] ) ? ':' : null, 'port' => null, 'path' => null, 'param' => isset( $parsed_url_array['query'] ) ? '?' : null, 'query' => null, 'hash' => isset( $parsed_url_array['fragment'] ) ? '#' : null, 'fragment' => null, ]; if(is_string($overwrite_parsed_url_array['query'])){ parse_str( $overwrite_parsed_url_array['query'], $overwrite_parsed_url_array['query'] ); } if ( isset( $overwrite_parsed_url_array['query'] ) && $merge_query_parameters === true ) { $overwrite_parsed_url_array['query'] = \array_merge_recursive( $overwrite_parsed_url_array['query'], $overwrite_parsed_url_array['query'] ); }elseif ( isset( $overwrite_parsed_url_array['query'] ) && $merge_query_parameters !== true ) { $overwrite_parsed_url_array['query'] = \array_merge( $overwrite_parsed_url_array['query'], $overwrite_parsed_url_array['query'] ); } $query_parameters = \http_build_query( $overwrite_parsed_url_array['query'], null, '&', \PHP_QUERY_RFC1738 ); $overwrite_parsed_url_array['query'] = \urldecode( preg_replace( '/%5B[0-9]+%5D/simU', '%5B%5D', $query_parameters ) ); $fully_parsed_url_array = \array_filter( \array_merge( $parsed_url_keys_array, $parsed_url_array, $overwrite_parsed_url_array ) ); return \implode( null, $fully_parsed_url_array ); } public function transport(string $url, string $method = 'GET', array $headers = null, array $options = null, array $httpOpts= null){ if(!is_string($this->userAgent)){ $userAgent = 'Webfan/Fusio-Plugin-0.0.1' .' Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML,like Gecko) Chrome/107.0.0.0 Safari/537.36'; $this->withUserAgent($userAgent); } foreach($this->urlRewriterMiddlewares as $rewriter){ $url = \call_user_func_array($rewriter, [$url]); } $callable = isset($this->_TRANSPORTS[$this->transport]) ? $this->_TRANSPORTS[$this->transport] : false; if(false === $callable){ throw new \Exception(sprintf('%sTransport is not set in %s. Use withTransport to define a callable for it!', ucfirst($this->transport), __METHOD__)); } if(is_array($callable) && !is_callable($callable)){ $fn = (\Closure::fromCallable($callable))->bindTo($this, \get_class($this)); $callable = $fn; }elseif(true !== $callable instanceof \Closure){ $fn = (\Closure::fromCallable($callable))->bindTo($this); $callable = $fn; } if(true === self::$increaseTimelimit){ set_time_limit(max(max($this->httTimeout,240), intval(ini_get('max_execution_time')) + max($this->httTimeout,240))); } $transport = \call_user_func_array($callable, func_get_args()); $status = ((string)$transport->status).''; if('3' === substr($status,0, 1)){ foreach($transport->headers as $i => $header){ $h = explode(':', $header, 2); $k = strtolower(trim($h[0])); $v = (isset($h[1])) ? trim($h[1]) : $header; if('location' === $k){ return $this->transport($v, $method , $headers, $options , $httpOpts); } } } return $transport; } protected function fetchCode($class, $salt = null) { if(!is_string($salt)){ $salt = $this->salt; } $url = $this->loadClass($class, $salt); if(is_bool($url)){ return $url; } $withSaltedUrl = (true === $this->str_contains($url, '${salt}', false)) ? true : false; $url = $this->replaceUrlVars($url, $salt, $class, $this->version); if(self::$increaseTimelimit){ set_time_limit(max(max($this->httTimeout,240), intval(ini_get('max_execution_time')) + max($this->httTimeout,240))); } $httpResult = $this->transport($url, 'GET', [ 'X-Source-Encoding'=>'b64', ], [ // 'ignore_errors' => true, 'timeout' => $this->httTimeout, ]); $code = $httpResult->body; if((false === $code || 200 != $httpResult->status) && (true === $this->str_contains($url, '/latest/', false) || true === $this->str_contains($url, '/stable/', false)) ){ $urlOld = $url; if(true === $this->str_contains($url, '/stable/', false)){ $url=preg_replace('/(\/stable\/)/', '/latest/', $url); }elseif(true === $this->str_contains($url, '/latest/', false)){ $url=preg_replace('/(\/latest\/)/', '/stable/', $url); } if($urlOld !== $url){ $httpResult = $this->transport($url, 'GET', [ 'X-Source-Encoding'=>'b64', ], [ // 'ignore_errors' => false, 'timeout' => $this->httTimeout, ]); $code = $httpResult->body; } //if(false===$code){ // return false; //} } if((false === $code || 200 != $httpResult->status) && (true === $this->str_contains($url, '/latest/', false) || true === $this->str_contains($url, '/stable/', false)) ){ $urlOld = $url; $url=preg_replace('/(\/stable\/)/', '/', $url); $url=preg_replace('/(\/latest\/)/', '/', $url); if($urlOld !== $url){ $httpResult = $this->transport($url, 'GET', [ 'X-Source-Encoding'=>'b64', ], [ // 'ignore_errors' => false, 'timeout' => $this->httTimeout, ]); $code = $httpResult->body; } if(false===$code){ return false; } } $json = false; $base64 = false; foreach($httpResult->headers as $i => $header){ $h = explode(':', $header, 2); $k = strtolower(trim($h[0])); $v = (isset($h[1])) ? trim($h[1]) : $header; if('x-content-hash' === $k){ $hash = $v; }elseif('x-user-hash' ===$k){ $userHash = $v; }elseif('content-type' ===$k && 'application/json'===$v){ $json = true; }elseif('x-data-encoding' ===$k && 'base64'===$v){ $base64 = true; } } if(true === $json){ $theJson =json_decode($code); $code = $theJson; $code=(array)$code; $code = $code['contents']; if(isset($theJson['X-Content-Hash'])){ $hash = $theJson['X-Content-Hash']; } if(isset($theJson['X-User-Hash'])){ $userHash = $theJson['X-User-Hash']; } } if(true === $base64){ $code = base64_decode($code); } if(false===$code || !is_string($code) || (true === $withSaltedUrl && true === $this->withSalt() && (!isset($hash) || !isset($userHash))) ){ // throw new \Exception('Missing checksums while fetching source code for '.$class.' from '.$url); error_log('Missing checksums while fetching source code for '.$class.' from '.$url, \E_USER_NOTICE); return false; } $oCode =$code; if(is_string($salt) && true === $withSaltedUrl && true === $this->withSalt() ){ $hash_check = strlen($oCode).'.'.sha1($oCode); $userHash_check = sha1($salt .$hash_check); if($hash_check !== $hash || $userHash_check !== $userHash){ // throw new \Exception('Invalid checksums while fetching source code for '.$class.' from '.$url); error_log('Invalid checksums while fetching source code for '.$class.' from '.$url .' However: ->withSalt() is DEPRECTATED, use ->withAfterMiddleware validators instead!', \E_USER_NOTICE); return false; } } if($this->is_base64($code) ){ $code = base64_decode($code); } // $code = trim($code); foreach($this->afterMiddlewares as $middleware){ if(( \is_callable($middleware[0]) || ('object' === gettype($middleware[0]) && $middleware[0] instanceof \Closure) ) && true !== \call_user_func_array($middleware[0], [$url, &$this, $class]) ){ continue; }elseif(is_string($middleware[0]) && !preg_match($middleware[0], $url)){ continue; } $code = call_user_func_array($middleware[1], [$code, &$this, $class]); if(!is_string($code)){ error_log('Untrusted source code for '.$class.' from '.$url.': INVALID SIGNATURE FOR: ' .htmlentities($code) , \E_USER_WARNING); if('object'===gettype($code) && $code instanceof \Exception){ throw $code; } return false; } } if(!$this->str_contains($code, '<?', false)){ // throw new \Exception('Invalid source code for '.$class.' from '.$url.': ' .htmlentities($code) ); error_log('Invalid source code for '.$class.' from '.$url.': ' .htmlentities($code) , \E_USER_NOTICE); return false; } if('<?php' === substr($code, 0, strlen('<?php')) ){ $code = substr($code, strlen('<?php'), strlen($code)); } $code = trim($code, '<?php> '); $codeWithStartTags = "<?php "."\n".$code; return $codeWithStartTags; } public function __invoke() { return call_user_func_array($this->getLoader(), func_get_args()); } public function is_base64($s){ // Check if there are valid base64 characters if (!preg_match('/^[a-zA-Z0-9\/\r\n+]*={0,2}$/', $s)) return false; // Decode the string in strict mode and check the results $decoded = base64_decode($s, true); if(false === $decoded) return false; // Encode the string again if(base64_encode($decoded) != $s) return false; return true; } public function register(bool $prepend = false) { if($this->isLoaderRegistered($isInOrder, $prepend) ) { return true; } $args = func_get_args(); //if(count($args)>=2 && is_bool($args[1])){ // $prepend = $args[1]; //} $throw = $prepend; /* Always FALSE as false is deprecated in SPL! This parameter is ignored as of PHP 8.0.0 !!! */ $res = false; // if(!$this->allowFromSelfOrigin && $this->domain === $this->selfDomain){ // throw new \Exception('You should not autoload from remote where you have local access to the source (remote server = host)'); // } // $aFuncs = \spl_autoload_functions(); // if(!is_array($aFuncs) || !in_array($this->getLoader(), $aFuncs) ){ // $res = \spl_autoload_register($this->getLoader(), $throw, $prepend); // } $res = $this->enable($prepend); if( false !== $res ){ // Change: Use a seperate process (or setup): $this->pruneCache(); }else{ throw new \Exception(sprintf('Cannot register Autoloader of "%s" with cachedir "%s"', __METHOD__, $this->cacheDir)); } return $res; } //https://github.com/johnstevenson/statical/blob/master/src/AliasManager.php protected function enable(?bool $prepend = false) { if(self::$alwaysAppendLoader === true){ $prepend = false; } $isRegistered = $this->isLoaderRegistered($isInOrder, $prepend); if (true===$isRegistered) { if ($isInOrder) { return $isRegistered; } // $this->disable(); } if (version_compare(\PHP_VERSION, '8.0.0') >= 0) { $isRegistered =true!==$prepend ? spl_autoload_register($this->getLoader(), true, $prepend) : spl_autoload_register($this->getLoader(), true, $prepend); }else{ $isRegistered = $prepend ? spl_autoload_register($this->getLoader(), true, $prepend) : spl_autoload_register($this->getLoader(), true, $prepend); } return $isRegistered; } protected function disable() { spl_autoload_unregister($this->getLoader()); } /** * Disables static proxying. * * @return void */ public function unregister() { $this->disable(); } //https://github.com/johnstevenson/statical/blob/master/src/AliasManager.php protected function isLoaderRegistered(&$isInOrder, ?bool $prepend = false) : bool { $registered = false; $isInOrder = false; if ($funcs = \spl_autoload_functions()) { $index = array_search($this->getLoader(), $funcs, true); if (false !== $index) { $registered = true; $isInOrder = true !== $prepend ? $index === count($funcs) - 1 : $index === 0; } } return $registered; } protected function getLoader() { return [$this, 'Autoload']; } public function pruneCache() { if($this->cacheLimit !== 0 && $this->cacheLimit !== -1){ $ShutdownTasks = \frdlweb\Thread\ShutdownTasks::mutex(); $ShutdownTasks(function($loader, int $cacheLimit){ @\ignore_user_abort(true); @\call_user_func_array([$loader, 'prune'], [$cacheLimit]); }, $this, $this->cacheLimit); } } public function prune(int $cacheLimit) { \webfan\hps\patch\Fs::pruneDir($this->cacheDir, $cacheLimit, true, 'tmp' !== basename($this->cacheDir)); } public function resolve(string $class):bool|string{ $cacheFile = $this->file($class); $url = $this->url( $class ); if($this->exists($cacheFile)){ return $cacheFile; }elseif(!is_bool($url) && $this->exists($url)){ return $url; }elseif( is_bool($url) ){ return $url; }else{ return false; } } public function file(string $class):bool|string{ return rtrim($this->cacheDir, \DIRECTORY_SEPARATOR.'/\\ '). \DIRECTORY_SEPARATOR. str_replace('\\', \DIRECTORY_SEPARATOR, $class). '.php'; } public function url(string $class):bool|string{ $salt = $this->salt; $url = $this->loadClass($class, $salt); if(is_bool($url)){ return $url; } $url = $this->replaceUrlVars($url, $salt, $class, $this->version); return $url; } public function Autoload(string $class):bool|string { foreach($this->beforeMiddlewares as $middleware){ if(false === \call_user_func_array($middleware, [$class, &$this]) ){ return false; } } $cacheFile = $this->file($class); if(file_exists($cacheFile) && ($this->cacheLimit !== 0 && $this->cacheLimit !== -1 && (filemtime($cacheFile) < time() - $this->cacheLimit) )){ unlink($cacheFile); clearstatcache(true, $cacheFile); } if(!file_exists($cacheFile) || ( $this->cacheLimit !== 0 && $this->cacheLimit !== -1 && (filemtime($cacheFile) < time() - $this->cacheLimit) ) ){ $code = $this->fetchCode($class, $this->salt); if(false===$code){ return false; }else if(true === $code){ return true; }elseif(false !==$code){ if(!is_dir(dirname($cacheFile))){ @mkdir(dirname($cacheFile), 0755, true); } if(!file_put_contents($cacheFile, $code)){ throw new \Exception('Cannot write source for class '.$class.' to '.$cacheFile); } }elseif(false ===$code && !file_exists($cacheFile)){ return false; } } if(file_exists($cacheFile) ){ if(false === ($this->requireFile($cacheFile)) ){ if(file_exists($cacheFile)){ unlink($cacheFile); } return false; } return class_exists($class, false); }elseif(isset($code) && is_string($code) && \frdlweb\Thread\ShutdownTasks::class !== $class ){ $tmpfile = tempnam($this->cacheDir, 'autoloaded-file.'.sha1($code)); $ShutdownTasks = \frdlweb\Thread\ShutdownTasks::mutex(); $ShutdownTasks(function($tmpfile){ if(file_exists($tmpfile)){ unlink($tmpfile); } }, $tmpfile); if(false === ($this->requireFile($tmpfile)) ){ if(file_exists($tmpfile)){ unlink($tmpfile); } return false; }else{ unlink($tmpfile); return class_exists($class, false); } }else{ throw new \Exception('Cannot write/load source for class '.$class.' in '.$cacheFile); } } } }//ns frdl\implementation\psr4