<?php
namespace Webfan\Psr4Loader;
class RemoteFromWebfan
{
protected $salted = true;
protected $selfDomain;
protected $server;
protected $domain;
protected $version;
protected $allowFromSelfOrigin = false;
protected static $instances = [];
protected static $classmap = [
\Wehowski\Gist\Http\Response\Helper::class => 'https://gist.githubusercontent.com/wehowski/d762cc34d5aa2b388f3ebbfe7c87d822/raw/5c3acdab92e9c149082caee3714f0cf6a7a9fe0b/Wehowski%255CGist%255CHttp%255CResponse%255CHelper.php?cache_bust=${salt}',
];
protected static $prefixes = [];
protected static $registeredGlobal = false;
public function __construct($server,// = 'frdl.webfan.de',
$register = true,
$version = 'latest',
$allowFromSelfOrigin = false,
$salted = true,
?array $classMap=[],
?string $prefix = '',
?bool $prependPrefix = false
){
$this->withSalt($salted);
$this->withClassmap($classMap);
$this->allowFromSelfOrigin = $allowFromSelfOrigin;
$this->version=$version;
$this->server = $server;
$_self = (isset($_SERVER['SERVER_NAME'])) ? $_SERVER['SERVER_NAME'] : $_SERVER['HTTP_HOST'];
$h = explode('.', $_self);
$dns = array_reverse($h);
$this->selfDomain = $dns[1].'.'.$dns[0];
$h = explode('.', $this->server);
$dns = array_reverse($h);
$this->domain = $dns[1].'.'.$dns[0];
if(!$this->allowFromSelfOrigin && $this->domain === $this->selfDomain){
$register = false;
}
if(true === $register){
$this->register();
$this->registerGlobal();
}
if(is_string($prefix)){
self::addNamespace($prefix, $this, $prependPrefix);
}
}
public function AutoloadGlobal($class){
$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);
if (isset(self::$prefixes[$prefix]) ) {
foreach (self::$prefixes[$prefix] as $server) {
if ($server->Autoload($relative_class)) {
return true;
}
}
}
$prefix = rtrim($prefix, '\\');
}
return false;
}
protected function registerGlobal(){
if(false !== self::$registeredGlobal){
return self::$registeredGlobal;
}
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)');
}
if(!in_array(self::getLoaderGlobal(), spl_autoload_functions()) ){
self::$registeredGlobal = !!(spl_autoload_register(self::getLoaderGlobal(), $throw, $prepend));
}
}
protected static function getLoaderGlobal(){
$firstMutex = (count(self::$instances)) ? self::$instances[0] : self::getInstance();
return [$firstMutex, 'AutoloadGlobal'];
}
public static function addNamespace($prefix, self &$server, $prepend = false)
{
// normalize namespace prefix
$prefix = trim($prefix, '\\') . '\\';
// normalize the base directory with a trailing separator
// initialize the namespace prefix array
if (isset(self::$prefixes[$prefix]) === false) {
self::$prefixes[$prefix] = [];
}
// retain the base directory for the namespace prefix
if ($prepend) {
array_unshift(self::$prefixes[$prefix], $server);
} else {
array_push(self::$prefixes[$prefix], $server);
}
}
public function withClassmap(array $classMap = null){
if(null !== $classMap){
foreach($classMap as $class => $server){
self::$classmap[$class] = $server;
}
}
return self::$classmap;
}
public function withSalt(bool $salted = null){
if(null !== $salted){
$this->salted = $salted;
}
return $this->salted;
}
public static function getInstance($server,// = 'frdl.webfan.de',
$register = false,
$version = 'latest',
$allowFromSelfOrigin = false,
$salted = true,
?array $classMap=[],
?string $prefix = '',
?bool $prependPrefix = false){
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['prefix'], $s['prependPrefix']);
}
return $arr;
}elseif(is_callable($server)){
$key = \spl_object_id($server);
}elseif(is_string($server)){
$key = $server;
}
if(!isset(self::$instances[$key])){
self::$instances[$key] = new self($server, $register, $version, $allowFromSelfOrigin, $salted, $prefix, $prependPrefix);
}
return self::$instances[$key];
}
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());
}
protected function fetchCode($class, $salt = null){
$server = (isset(self::$classmap[$class]))
? self::$classmap[$class]
: $this->server;
if(!is_string($salt) && true === $this->withSalt()){
$salt = mt_rand(10000000,99999999);
}
$class = str_replace('\\', '/', $class);
if(is_callable($server)){
$url = call_user_func_array($server, [$class, $this->version, $salt]);
}elseif(substr($server, 0, strlen('http://')) === 'http://' || substr($server, 0, strlen('https://')) === 'https://'){
$url = str_replace(['${salt}', '${class}', '${version}'], [$salt, $class, $this->version], $server);
}else{
$url = 'https://'.$server.'/install/?salt='.$salt.'&source='. $class.'&version='.$this->version;
}
$options = [
'https' => [
'method' => 'GET',
'ignore_errors' => true,
]
];
$context = stream_context_create($options);
$code = @file_get_contents($url, false, $context);
foreach($http_response_header as $i => $header){
$h = explode(':', $header);
if('x-content-hash' === strtolower(trim($h[0]))){
$hash = trim($h[1]);
}
if('x-user-hash' === strtolower(trim($h[0]))){
$userHash = trim($h[1]);
}
}
if(false===$code || !is_string($code) || (true === $this->withSalt() && (!isset($hash) || !isset($userHash)))){
return false;
}
$oCode =$code;
$hash_check = strlen($oCode).'.'.sha1($oCode);
$userHash_check = sha1($salt .$hash_check);
if(false!==$salt && true === $this->withSalt()){
if($hash_check !== $hash || $userHash_check !== $userHash){
throw new \Exception('Invalid checksums while fetching source code for '.$class.' from '.$url);
}
}
$code = trim($code);
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());
}
protected function register($throw = true, $prepend = 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)');
}
if(!in_array($this->getLoader(), spl_autoload_functions()) ){
return spl_autoload_register($this->getLoader(), $throw, $prepend);
}
}
protected function getLoader(){
return [$this, 'Autoload'];
}
public function Autoload($class){
$cacheFile = ((isset($_ENV['FRDL_HPS_PSR4_CACHE_DIR'])) ? $_ENV['FRDL_HPS_PSR4_CACHE_DIR']
: sys_get_temp_dir() . \DIRECTORY_SEPARATOR. 'psr4'. \DIRECTORY_SEPARATOR
)
. str_replace('\\', \DIRECTORY_SEPARATOR, $class). '.php';
if(file_exists($cacheFile)
&& (!isset($_ENV['FRDL_HPS_PSR4_CACHE_LIMIT'])
|| (filemtime($cacheFile) > time() - ((isset($_ENV['FRDL_HPS_PSR4_CACHE_LIMIT']) ) ? intval($_ENV['FRDL_HPS_PSR4_CACHE_LIMIT']) : 3 * 60 * 60)) )){
require $cacheFile;
return true;
}
$code = $this->fetchCode($class, null);
if(false !==$code){
if(!is_dir(dirname($cacheFile))){
mkdir(dirname($cacheFile), 0755, true);
}
if(isset($_ENV['FRDL_HPS_PSR4_CACHE_LIMIT'])
&& file_exists($cacheFile)
&& (filemtime($cacheFile) < time() - ((isset($_ENV['FRDL_HPS_PSR4_CACHE_LIMIT']) ) ? intval($_ENV['FRDL_HPS_PSR4_CACHE_LIMIT']) : 3 * 60 * 60)) ){
unlink($cacheFile);
}
// if(!file_put_contents($cacheFile, $code)){
// throw new \Exception('Cannot write '.$url.' to '.$cacheFile);/* error_log('Cannot write '.$url.' to '.$cacheFile, \E_WARNING); */
// }
file_put_contents($cacheFile, $code);
}//if(false !==$code)
if(file_exists($cacheFile) ){
if(false === (require $cacheFile)){
unlink($cacheFile);
}
return true;
}elseif(false !==$code){
$code =ltrim($code, '<?php');
$code =rtrim($code, '?php>');
eval($code);
return true;
}
}
}
|