/**
  * 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;
 }