/** * Run application * * @param array $options * string application_name * string application_path * string ini_folder * boolean __run_only_bootstrap * @throws Exception */ public static function run($options = []) { // fixing location paths $application_path = isset($options['application_path']) ? rtrim($options['application_path'], '/') . '/' : '../application/'; $application_name = isset($options['application_name']) ? $options['application_name'] : 'default'; $ini_folder = isset($options['ini_folder']) ? rtrim($options['ini_folder'], '/') . '/' : $application_path . 'config/'; // working directory is location of the application chdir($application_path); $application_path_full = getcwd(); // setting include_path $paths = []; $paths[] = $application_path_full; $paths[] = __DIR__; $paths[] = str_replace('/numbers/framework', '', __DIR__); set_include_path(implode(PATH_SEPARATOR, $paths)); // support functions require "functions.php"; // load ini settings self::$settings = system_config::load($ini_folder); // special handling of media files for development, so there's no need to redeploy application if (self::$settings['environment'] == 'development' && isset($_SERVER['REQUEST_URI'])) { system_media::serve_media_if_exists($_SERVER['REQUEST_URI'], $application_path); } // we need to solve chicken and egg problem so we load cache first and then run application //cache::create('php', array('type'=>'php', 'dir'=>'../application/cache')); // setting variables if (!isset(self::$settings['application']) || !is_array(self::$settings['application'])) { self::$settings['application'] = []; } self::$settings['application']['name'] = $application_name; self::$settings['application']['path'] = $application_path; self::$settings['application']['path_full'] = $application_path_full . '/'; self::$settings['application']['loaded_classes'] = []; // class paths self::$settings['layout'] = []; // layout settings // flags self::$settings['flag'] = isset(self::$settings['flag']) && is_array(self::$settings['flag']) ? self::$settings['flag'] : []; self::$settings['flag']['global']['__run_only_bootstrap'] = !empty($options['__run_only_bootstrap']); // magic variables processed here self::$settings['flag']['global']['__content_type'] = 'text/html'; self::process_magic_variables(); // processing php settings if (isset(self::$settings['php'])) { foreach (self::$settings['php'] as $k => $v) { if (is_array($v)) { foreach ($v as $k2 => $v2) { if (is_numeric($v2)) { $v2 = $v2 * 1; } ini_set($k . '.' . $k2, $v2); } } else { if (is_numeric($v)) { $v = $v * 1; } ini_set($k, $v); } } } // Destructor register_shutdown_function(array('bootstrap', 'destroy')); // error handler first error_base::init(); // debug after error handler debug::init(self::get('debug')); // Bootstrap Class $bootstrap = new bootstrap(); $bootstrap_methods = get_class_methods($bootstrap); foreach ($bootstrap_methods as $method) { if (strpos($method, 'init') === 0) { call_user_func(array($bootstrap, $method), $options); } } // if we are calling application from the command line if (!empty($options['__run_only_bootstrap'])) { // dispatch before, in case if we open database connections in there if (!empty(self::$settings['application']['dispatch']['before_controller'])) { call_user_func(self::$settings['application']['dispatch']['before_controller']); } return; } // processing mvc settings self::set_mvc(); // check if controller exists if (!file_exists(self::$settings['mvc']['controller_file'])) { throw new Exception('Resource not found!', -1); } // initialize the controller $controller_class = self::$settings['mvc']['controller_class']; $controller = new $controller_class(); self::$settings['controller'] = get_object_vars($controller); // dispatch before, we need some settings from the controller if (!empty(self::$settings['application']['dispatch']['before_controller'])) { call_user_func(self::$settings['application']['dispatch']['before_controller']); } // singleton start if (!empty(self::$settings['controller']['singleton_flag'])) { $message = !empty(self::$settings['controller']['singleton_message']) ? self::$settings['controller']['singleton_message'] : 'This script is being run by another user!'; $lock_id = "singleton_" . $controller_class; if (lock::process($lock_id) === false) { throw new Exception($message); } } // process parameters and provide output self::process(); // release singleton lock if (!empty(self::$settings['controller']['singleton_flag'])) { lock::release($lock_id); } // dispatch after controller if (!empty(self::$settings['application']['dispatch']['after_controller'])) { call_user_func(self::$settings['application']['dispatch']['after_controller']); } // headers if (!empty(self::$settings['header']) && !headers_sent()) { foreach (self::$settings['header'] as $k => $v) { header($v); } } }
/** * Process dependencies * * @param array $options * @return array */ public static function process_deps_all($options = []) { $result = ['success' => false, 'error' => [], 'data' => []]; do { // processing main dependency file $main_dep_filename = 'config/application.ini'; if (!file_exists($main_dep_filename)) { $result['error'][] = "Main dep. file not found!"; break; } // some array arrangements $data = system_config::ini($main_dep_filename, 'dependencies'); $data = $data['dep'] ?? []; $data['composer'] = $data['composer'] ?? []; $data['submodule'] = $data['submodule'] ?? []; $data['submodule_dirs'] = []; $data['apache'] = $data['apache'] ?? []; $data['php'] = $data['php'] ?? []; $data['model'] = $data['model'] ?? []; $data['__model_dependencies'] = []; $data['model_import'] = []; $data['override'] = $data['override'] ?? []; $data['acl'] = $data['acl'] ?? []; $data['media'] = $data['media'] ?? []; $data['model_processed'] = []; $data['unit_tests'] = []; $data['__submodule_dependencies'] = []; $dummy = []; // we have small chicken and egg problem with composer $composer_data = []; $composer_dirs = []; $composer_dirs[] = 'config/'; if (file_exists('../libraries/composer.json')) { $composer_data = json_decode(file_get_contents('../libraries/composer.json'), true); } // if we have composer or submodules from main dep file if (!empty($data['composer']) || !empty($data['submodules'])) { $composer_data['require'] = []; if (!empty($data['composer'])) { self::process_deps_array($data['composer'], $composer_data['require'], $composer_dirs, 'dummy', $dummy); } if (!empty($data['submodule'])) { self::process_deps_array($data['submodule'], $composer_data['require'], $composer_dirs, 'dummy', $dummy); } } // processing submodules $mutex = []; $__any = []; if (!empty($composer_dirs)) { for ($i = 0; $i < 3; $i++) { foreach ($composer_dirs as $k => $v) { if (isset($mutex[$k])) { continue; } else { $mutex[$k] = 1; } if (file_exists($v . 'module.ini')) { $data['submodule_dirs'][$v] = $v; $sub_data = system_config::ini($v . 'module.ini', 'dependencies'); $sub_data = isset($sub_data['dep']) ? $sub_data['dep'] : []; if (!empty($sub_data['composer'])) { self::process_deps_array($sub_data['composer'], $composer_data['require'], $composer_dirs, $k, $dummy); $data['composer'] = array_merge2($data['composer'], $sub_data['composer']); } if (!empty($sub_data['submodule'])) { self::process_deps_array($sub_data['submodule'], $composer_data['require'], $composer_dirs, $k, $data['__submodule_dependencies']); $data['submodule'] = array_merge2($data['submodule'], $sub_data['submodule']); } if (!empty($sub_data['apache'])) { $data['apache'] = array_merge2($data['apache'], $sub_data['apache']); } if (!empty($sub_data['php'])) { $data['php'] = array_merge2($data['php'], $sub_data['php']); } if (!empty($sub_data['model'])) { $data['model'] = array_merge2($data['model'], $sub_data['model']); $temp = []; array_keys_to_string($sub_data['model'], $temp); foreach ($temp as $k0 => $v0) { $data['__model_dependencies'][$k][$k0] = $k0; } } if (!empty($sub_data['override'])) { $data['override'] = array_merge2($data['override'], $sub_data['override']); } if (!empty($sub_data['acl'])) { $data['acl'] = array_merge2($data['acl'], $sub_data['acl']); } if (!empty($sub_data['media'])) { $data['media'] = array_merge2($data['media'], $sub_data['media']); } // processing unit tests if (file_exists($v . 'unit_tests')) { // we have to reload the module.ini file to get module name $sub_data_temp = system_config::ini($v . 'module.ini', 'module'); $data['unit_tests'][$sub_data_temp['module']['name']] = $v . 'unit_tests/'; } } else { $keys = explode('/', $k); $last = end($keys); if ($last == '__any') { $temp2 = []; foreach ($keys as $v2) { if ($v2 != '__any') { $temp2[] = $v2; } } $__any[$k] = $temp2; } else { if ($keys[0] == 'numbers') { $result['error'][] = " - Submodule not found in {$v}module.ini"; } } } } } } // processing any dependencies if (!empty($__any)) { foreach ($__any as $k => $v) { $temp = array_key_get($data['submodule'], $v); unset($temp['__any']); if (empty($temp)) { $result['error'][] = " - Any dependency required {$k}!"; } } } // processing composer if (!empty($composer_data['require'])) { foreach ($composer_data['require'] as $k => $v) { if (!file_exists('../libraries/vendor/' . $k)) { $result['error'][] = " - Composer library \"{$k}\" is not loaded!"; } } } // sometimes we need to make sure we have functions available $func_per_extension = ['pgsql' => 'pg_connect']; // proceccing php extensions if (!empty($data['php']['extension'])) { foreach ($data['php']['extension'] as $k => $v) { if (isset($func_per_extension[$k]) && function_exists($func_per_extension[$k]) == false || !extension_loaded($k)) { $result['error'][] = " - PHP extension \"{$k}\" is not loaded!"; } } } // processing php ini settings if (!empty($data['php']['ini'])) { foreach ($data['php']['ini'] as $k => $v) { foreach ($v as $k2 => $v2) { $temp = ini_get($k . '.' . $k2); if (ini_get($k . '.' . $k2) != $v2) { $result['error'][] = " - PHP ini setting {$k}.{$k2} is \"{$temp}\", should be {$v2}!"; } } } } // processing apache modules if (!empty($data['apache']['module'])) { if (function_exists('apache_get_modules')) { $ext_have = array_map('strtolower', apache_get_modules()); } else { $temp = `apachectl -t -D DUMP_MODULES`; $ext_have = array_map('strtolower', explode("\n", $temp)); $temp = array(); foreach ($ext_have as $k => $v) { $temp[] = trim(str_replace(array('(shared)', '(static)'), '', $v)); } $ext_have = $temp; } foreach ($data['apache']['module'] as $k => $v) { if (!in_array($k, $ext_have)) { $result['error'][] = " - Apache module \"{$k}\" is not loaded!"; } } } // processing models if (!empty($data['model'])) { array_keys_to_string($data['model'], $data['model_processed']); } // processing imports, we need to sort them in order of dependencies $imports = []; foreach ($data['model_processed'] as $k => $v) { if ($v != 'object_import') { continue; } // find submodule foreach ($data['__model_dependencies'] as $k2 => $v2) { if (!empty($v2[$k])) { $imports[$k2][$k] = $k; break; } } } // clean up unused dependencies foreach ($data['__submodule_dependencies'] as $k2 => $v2) { if (empty($imports[$k2])) { $data['__submodule_dependencies'][$k2] = []; } else { foreach ($v2 as $k3 => $v3) { if (empty($imports[$k3])) { unset($data['__submodule_dependencies'][$k2][$k3]); } } } } // we need to go though an array few times to fix dependency issues for ($i = 0; $i < 3; $i++) { foreach ($imports as $k => $v) { if (empty($data['__submodule_dependencies'][$k])) { $data['model_import'][$k] = $v; // we need to remove file from dependency foreach ($data['__submodule_dependencies'] as $k2 => $v2) { unset($data['__submodule_dependencies'][$k2][$k]); } } } } foreach ($data['model_import'] as $k => $v) { foreach ($v as $k2 => $v2) { unset($data['model_processed'][$k2]); $data['model_processed'][$k2] = 'object_import'; } } unset($data['__submodule_dependencies'], $data['__model_dependencies'], $data['model_import']); // handling overrides, cleanup directory first helper_file::rmdir('./overrides/class', ['only_contents' => true, 'skip_files' => ['.gitkeep']]); $data['override'] = array_merge_hard($data['override'], $data['acl']); if (!empty($data['override'])) { array_keys_to_string($data['override'], $data['override_processed']); $override_classes = []; $override_found = false; foreach ($data['override_processed'] as $k => $v) { if (!isset($override_classes[$v])) { $override_classes[$v] = ['object' => new object_override_blank(), 'found' => false]; } $override_class = str_replace('.', '_', $k); $override_object = new $override_class(); $vars = get_object_vars($override_object); if (!empty($vars)) { $override_classes[$v]['found'] = true; $override_found = true; object_merge_values($override_classes[$v]['object'], $vars); } } // we need to write overrides to disk if ($override_found) { foreach ($override_classes as $k => $v) { if ($v['found']) { $class_code = "<?php\n\n" . '$object_override_blank_object = ' . var_export($v['object'], true) . ';'; helper_file::write('./overrides/class/override_' . $k . '.php', $class_code); } } } } // unit tests helper_file::rmdir('./overrides/unit_tests', ['only_contents' => true, 'skip_files' => ['.gitkeep']]); // submodule tests first if (!empty($data['unit_tests'])) { $xml = ''; $xml .= '<phpunit bootstrap="../../../libraries/vendor/numbers/framework/system/managers/unit_tests.php">'; $xml .= '<testsuites>'; foreach ($data['unit_tests'] as $k => $v) { $xml .= '<testsuite name="' . $k . '">'; foreach (helper_file::iterate($v, ['recursive' => true, 'only_extensions' => ['php']]) as $v2) { $xml .= '<file>../../' . $v2 . '</file>'; } $xml .= '</testsuite>'; } $xml .= '</testsuites>'; $xml .= '</phpunit>'; helper_file::write('./overrides/unit_tests/submodules.xml', $xml); } // application test last $application_tests = helper_file::iterate('misc/unit_tests', ['recursive' => true, 'only_extensions' => ['php']]); if (!empty($application_tests)) { $xml = ''; $xml .= '<phpunit bootstrap="../../../libraries/vendor/numbers/framework/system/managers/unit_tests.php">'; $xml .= '<testsuites>'; $xml .= '<testsuite name="application/unit/tests">'; foreach ($application_tests as $v) { $xml .= '<file>../../' . $v . '</file>'; } $xml .= '</testsuite>'; $xml .= '</testsuites>'; $xml .= '</phpunit>'; helper_file::write('./overrides/unit_tests/application.xml', $xml); } // updating composer.json file if ($options['mode'] == 'commit') { helper_file::write('../libraries/composer.json', json_encode($composer_data, JSON_PRETTY_PRINT)); } // assinging variables to return to the caller $result['data'] = $data; if (empty($result['error'])) { $result['success'] = true; } } while (0); return $result; }