services/_yf_autoloader.php
<?php
class yf_autoloader {
public $libs_root = '';
public $is_console = false;
public $config_names = [
'file',
'composer_names',
'git_urls',
'pear',
'autoload_config',
'require_once',
'manual',
'require_services',
'example',
];
public $composer_names = [];
public $git_urls = [];
public $autoload_config = [];
public $pear = [];
public $require_services = [];
public $manual = [];
public $require_once = [];
public $example = [];
/***/
public function __construct($config = []) {
!defined('YF_PATH') && define('YF_PATH', dirname(dirname(__DIR__)).'/');
$this->libs_root = YF_PATH.'vendor/';
$this->is_console = $_SERVER['argc'] && !isset($_SERVER['REQUEST_METHOD']);
foreach ($this->config_names as $name) {
$this->$name = $config[$name];
}
$this->process_composer();
$this->process_git();
$this->process_pear();
$this->process_yf_autoload();
$this->process_require_services();
$this->process_require_once();
$this->process_manual();
$this->process_example();
}
/***/
public function process_example() {
$libs_root = $this->libs_root;
$example = $this->example;
if (!is_callable($example)) {
return false;
}
if ($this->is_console) {
$trace = debug_backtrace();
if (realpath($_SERVER['argv'][0]) === realpath($trace[1]['file'])) {
try {
$example($this);
} catch (Exception $e) {
var_dump($e);
}
}
}
}
/***/
public function process_composer() {
$libs_root = $this->libs_root;
$composer_names = $this->composer_names;
if (!$composer_names) {
return false;
}
$dir = $libs_root.'vendor/';
foreach ((array)$composer_names as $composer_package) {
$check_file = $dir. dirname($composer_package).'/'.basename($composer_package).'/';
if (!file_exists($check_file)) {
$this->composer_require($composer_package);
$this->check_error($composer_package, $dir, $check_file, 'something wrong with composer');
}
}
require_once $dir. 'autoload.php';
// Exclude raw git clone steps
$this->git_urls = [];
$this->autoload_config = [];
}
/***/
public function process_git() {
$libs_root = $this->libs_root;
$git_urls = $this->git_urls;
foreach ((array)$git_urls as $git_url => $lib_dir) {
$dir = $libs_root. $lib_dir;
$check_file = $dir.'.git';
if (!file_exists($check_file)) {
if (false !== strpos($git_url, '~')) {
list($git_url, $git_tag) = explode('~', $git_url);
$cmd = '(git clone --depth 1 --branch "'.$git_tag.'" --single-branch '.$git_url.' '.$dir.' && cd '.$dir.' && git checkout "'.$git_tag.'")';
} else {
$cmd = 'git clone --depth 1 '.$git_url.' '.$dir;
}
passthru($cmd);
$this->check_error(basename($lib_dir), $dir, $check_file);
} elseif (getenv('YF_FORCE_UPDATE_SERVICES')) {
if (false !== strpos($git_url, '~')) {
// Tag was forced, so do nothing
} else {
$cmd = 'cd '.$dir.' && git pull';
}
passthru($cmd);
}
}
}
/***/
public function process_pear() {
if (!$this->pear) {
return false;
}
spl_autoload_register(function($class){
$libs_root = $this->libs_root;
$pear_config = $this->pear;
foreach ((array)$pear_config as $lib_dir => $prefix) {
if (strlen($prefix) && strpos($class, $prefix) !== 0) {
continue;
}
$path = $libs_root. $lib_dir. str_replace('_', '/', $class).'.php';
if (file_exists($path)) {
require $path;
}
return true;
}
});
}
/***/
public function process_yf_autoload() {
$libs_root = $this->libs_root;
$autoload_config = $this->autoload_config;
if (!$autoload_config) {
return false;
}
spl_autoload_register(function($class) {
$libs_root = $this->libs_root;
$autoload_config = $this->autoload_config;
foreach ((array)$autoload_config as $lib_dir => $prefix) {
$no_cut_prefix = false;
if (substr($prefix, 0, strlen('no_cut_prefix:')) === 'no_cut_prefix:') {
$no_cut_prefix = true;
}
if (false !== strpos($prefix, ':')) {
list($tmp, $prefix) = explode(':', $prefix);
}
if (strpos($class, $prefix) !== 0) {
continue;
}
if ($no_cut_prefix) {
$path = $libs_root. $lib_dir. str_replace("\\", '/', $class).'.php';
} else {
$path = $libs_root. $lib_dir. str_replace("\\", '/', substr($class, strlen($prefix) + 1)).'.php';
}
if (!file_exists($path)) {
continue;
}
require $path;
return true;
}
});
}
/***/
public function process_require_once() {
$libs_root = $this->libs_root;
$require_once = $this->require_once;
if (!$require_once) {
return false;
}
foreach ((array)$require_once as $path) {
require_once $libs_root. $path;
}
}
/**
*/
function _get_lib_path($name, $params = []) {
if (isset($this->php_libs[$name])) {
return $this->php_libs[$name];
}
if (!isset($this->_paths_cache)) {
$suffix = '.php';
$pattern = '{,plugins/*/}{services/,share/services/}{*,*/*}'. $suffix;
$globs = [
'framework' => YF_PATH. $pattern,
];
defined('APP_PATH') && $globs['app'] = APP_PATH. $pattern;
$slen = strlen($suffix);
$paths = [];
foreach((array)$globs as $gname => $glob) {
foreach(glob($glob, GLOB_BRACE) as $_path) {
$_name = substr(basename($_path), 0, -$slen);
if (!$_name == '_yf_autoloader') {
continue;
}
$paths[$_name] = $_path;
}
}
// This double iterating code ensures we can inherit/replace services with same name inside project
foreach((array)$paths as $_name => $_path) {
$this->_paths_cache[$_name] = $_path;
}
}
$this->php_libs[$name] = $path;
return $this->_paths_cache[$name];
}
/***/
public function process_require_services() {
$libs_root = $this->libs_root;
$require_services = $this->require_services;
if (!$require_services) {
return false;
}
ob_start();
foreach ((array)$require_services as $name) {
require_once $this->_get_lib_path($name);
}
ob_end_clean();
}
/***/
public function process_manual() {
$libs_root = $this->libs_root;
$manual = $this->manual;
if (is_callable($manual)) {
$manual($this);
}
}
// globally: curl -s http://getcomposer.org/installer | php -- --install-dir=/usr/local/bin
// locally: curl -s http://getcomposer.org/installer | php
// ls -s /usr/local/bin/composer.phar /usr/local/bin/composer
public function composer_require($package) {
$libs_root = $this->libs_root;
set_error_handler(function ($code, $msg) {
// do nothing for these types of errors
}, E_NOTICE | E_USER_NOTICE | E_STRICT | E_DEPRECATED | E_USER_DEPRECATED);
ob_start();
include_once __DIR__.'/composer.php';
ob_end_clean();
$cwd = getcwd();
chdir($libs_root);
$input = new Symfony\Component\Console\Input\ArrayInput(['command' => 'require', 'packages' => is_array($package) ? $package : [$package]]);
$input->setInteractive(false);
$application = new Composer\Console\Application();
$application->setAutoExit(false);
$application->run($input);
restore_error_handler();
chdir($cwd);
}
/***/
public function check_error($name, $dir, $check_file, $error_reason = 'git url or command is wrong') {
$libs_root = $this->libs_root;
$error_reasons = [];
if (!file_exists($check_file)) {
if (!is_writable($dir)) {
$error_reasons[] = $dir.' is not writable';
if (!is_readable($dir)) {
$error_reasons[] = $dir.' is not readable';
} else {
$stat = stat($dir);
$posix = posix_getpwuid($stat['uid']);
$error_reasons[] = ', details: file owner: '.$posix['name'].', php owner: '.$_SERVER['USER'].', file perms: '.fileperms($dir);
}
}
}
if ($error_reasons) {
throw new Exception('lib "'.$name.'" install failed. Reasons: '.implode(', ', $error_reasons));
}
}
}