Procházet zdrojové kódy

Config class rewrite (#6965)

* Use a singleton pattern in the database class to re-use the database object inside the permission_exists function.
frytimo před 1 rokem
rodič
revize
4600383275

+ 1 - 4
core/default_settings/default_settings.php

@@ -124,7 +124,7 @@
 		$sql .= "lower(default_setting_category) = :default_setting_category ";
 		$parameters['default_setting_category'] = strtolower($default_setting_category);
 	}
-	$database = new database;
+	$database = database::new();
 	$num_rows = $database->select($sql, $parameters ?? null, 'column');
 
 //get the list
@@ -148,7 +148,6 @@
 	}
 	$sql .= order_by($order_by, $order, 'default_setting_category, default_setting_subcategory, default_setting_order', 'asc');
 	//$sql .= limit_offset($rows_per_page, $offset ?? '');  //$offset is always null
-	$database = new database;
 	$default_settings = $database->select($sql, $parameters ?? null, 'all');
 	unset($sql, $parameters);
 
@@ -173,7 +172,6 @@
 	$sql .= ") as quantity ";
 	$sql .= "from v_default_settings as d1 ";
 	$sql .= "order by d1.default_setting_category asc ";
-	$database = new database;
 	$rows = $database->select($sql, $parameters ?? null, 'all');
 	if (!empty($rows) && @sizeof($rows) != 0) {
 		foreach ($rows as $row) {
@@ -462,7 +460,6 @@
 				$sql = "select * from v_menus ";
 				$sql .= "where menu_uuid = :menu_uuid ";
 				$parameters['menu_uuid'] = $row['default_setting_value'];
-				$database = new database;
 				$sub_result = $database->select($sql, $parameters ?? null, 'all');
 				foreach ($sub_result as &$sub_row) {
 					echo $sub_row["menu_language"]." - ".$sub_row["menu_name"]."\n";

+ 305 - 115
resources/classes/config.php

@@ -1,155 +1,345 @@
 <?php
 
 /**
- * config
- *
- * @method get config.php
- * @method find find the path to the config.php file
- * @method exists determin if the the config.php file exists
+ * config class loads configuration from the file system
+ * @param string $db_type Type of database
+ * @param string $db_driver Alias of type
+ * @param string $db_host Host to connect to
+ * @param string $db_path Path of the database if it is file system based
+ * @param string $db_file File name of the database if it is file system based
+ * @param string $db_port Port to connect to
+ * @param string $db_name Name of the database
+ * @param string $db_sslmode SSL Mode to use
+ * @param string $db_cert_authority The certificate authority
+ * @param string $db_secure If the database is using a secure connection
+ * @param string $db_username Username credentials to connect with
+ * @param string $db_password Password credentials to connect with
+ * @param string $config_path Configuration path currently in use
+ * @param string $config_file Configuration file currently in use
+ * @param string $config_path_and_filename Full path and configuration file currently in use
+ * @internal the @param statements are used because they match the magic __get function that allows those to be accessed publicly
  */
-class config {
+final class config {
+
+	// Full path and filename of config.conf
+	private $file;
+
+	// The internal array that holds the configuration in the config.conf file
+	private $configuration;
 
 	/**
-	 * database variables and config path
+	 * Configuration object used to hold a single instance
+	 * @var array
 	 */
-	public $db_type;
-	public $db_name;
-	public $db_username;
-	public $db_password;
-	public $db_sslmode;
-	public $db_host;
-	public $db_path;
-	public $db_port;
-	public $db_secure;
-	public $db_cert_authority;
-	public $config_path;
+	public static $config = null;
 
 	/**
-	 * Called when the object is created
+	 * Loads the framework configuration file
 	 */
-	public function __construct() {
-		//place holder
+	public function __construct(?string $file = '') {
+
+		//initialize configuration array to be an empty array
+		$this->configuration = [];
+
+		//check if the config file was found
+		if (empty($file)) {
+			//locate the conf file
+			$file = self::find();
+		}
+
+		//remember the fullpath and filename
+		$this->file = $file;
+
+		//load the conf file
+		if (file_exists($file)) {
+			$this->read();
+		}
+
+		//set the server variables
+		$this->define_project_paths();
 	}
 
 	/**
-	 * Determine whether the config file exists
-	 * @var string $db_type - type of database
-	 * @var string $db_name - name of the database
-	 * @var string $db_username - username to access the database
-	 * @var string $db_password - password to access the database
-	 * @var string $db_host - hostname of the database server
-	 * @var string $db_path - path of the database file
-	 * @var string $db_port - network port to connect to the database
-	 * @var bool $db_secure - whether or not to connect with SSL
-	 * @var string $db_cert_authority - location of certificate authority
+	 * Magic method to allow backward compatibility for variables such as db_type.
+	 * <p>This will allow using config object with the syntax of:<br>
+	 * $config = new config();<br>
+	 * $db_type = $config->db_type;<br></p>
+	 * <p>Note:<br>
+	 * The <i>InvalidArgumentException</i> is thrown if there is no such variable accessed such as:<br>
+	 * $config = new config();<br>
+	 * $db_function = $config->db_function();
+	 * </p>
+	 * <p>This is ensure that any invalid code is detected and fixed.</p>
+	 * @param string $name Name of the object property
+	 * @return string Returns the value as a string
 	 */
-	public function get() {
-		//find the config_path
-		$config_path = $this->find();
+	public function __get(string $name): string {
+		switch($name) {
+			case 'db_type':
+			case 'db_driver':
+				return $this->configuration['database.0.type'] ?? '';
+			case 'db_path':
+			case 'path':
+				return $this->configuration['database.0.path'] ?? '';
+			case 'db_host':
+				return $this->configuration['database.0.host'] ?? '';
+			case 'db_port':
+				return $this->configuration['database.0.port'] ?? '';
+			case 'db_name':
+				return $this->configuration['database.0.name'] ?? '';
+			case 'db_sslmode':
+				return $this->configuration['database.0.sslmode'] ?? 'prefer';
+			case 'db_cert_authority':
+				return $this->configuration['database.0.cert_authority'] ?? '';
+			case 'db_secure':
+				return $this->configuration['database.0.secure'] ?? 'false';
+			case 'db_username':
+			case 'username':
+				return $this->configuration['database.0.username'] ?? '';
+			case 'db_password':
+			case 'password':
+				return $this->configuration['database.0.password'] ?? '';
+			case 'db_file':
+				return $this->configuration['database.0.file'] ?? '';
+			case 'config_path':
+				return $this->path();
+			case 'config_filename':
+				return $this->filename();
+			case 'config_path_and_filename':
+			case 'config_file':
+				return $this->path_and_filename();
+			default:
+				if (property_exists($this, $name)) {
+					return $this->{$name};
+				}
+				elseif (array_key_exists($name, $this->configuration)) {
+					return $this->configuration[$name];
+				}
+		}
+		return "";
+	}
 
-		//add the document root to the include path
-		$conf = parse_ini_file($config_path);
-		set_include_path($conf['document.root']);
+	/**
+	 * Returns the string representation of the configuration file
+	 * @return string configuration
+	 */
+	public function __toString(): string {
+		$string_builder = "";
+		foreach ($this->configuration as $key => $value) {
+			$string_builder .= "$key = '$value'\n";
+		}
+		return $string_builder;
+	}
+
+	// loads the config.conf file
+	public function read() {
 
-		//check if the config file exists
-		$config_exists = file_exists($config_path) ? true : false;
-		
+		//check if include is needed
+		if (substr($this->file, 0, -4) === '.php') {
+			//allow global variables to be set in the old config.php file
+			global $db_type, $db_host, $db_port, $db_name, $db_username, $db_password, $db_path;
+			global $db_sslmode, $db_secure, $db_cert_authority;
+
+			//load the config.php file
+			require_once $this->file;
+
+			//convert the old properties to the new standard
+			if (isset($db_type)) {
+				$this->configuration['database.0.type'] = $db_type;
+			} else {
+				$this->configuration['database.0.type'] = 'pgsql';
+			}
+			if (isset($db_path)) {
+				$this->configuration['database.0.path'] = $db_path;
+			} else {
+				$this->configuration['database.0.path'] = '';
+			}
+			if (isset($db_host)) {
+				$this->configuration['database.0.host'] = $db_host;
+			}
+			if (isset($db_port)) {
+				$this->configuration['database.0.port'] = $db_port;
+			}
+			if (isset($db_name)) {
+				$this->configuration['database.0.name'] = $db_name;
+			}
+			if (isset($db_username)) {
+				$this->configuration['database.0.username'] = $db_username;
+			}
+			if (isset($db_password)) {
+				$this->configuration['database.0.password'] = $db_password;
+			}
+			if (isset($db_sslmode)) {
+				$this->configuration['database.0.sslmode'] = $db_sslmode;
+			} else {
+				$this->configuration['database.0.sslmode'] = 'prefer';
+			}
+			if (isset($db_secure)) {
+				$this->configuration['database.0.secure'] = $db_secure;
+			}
+			if (isset($db_cert_authority)) {
+				$this->configuration['database.0.cert_authority'] = $db_cert_authority;
+			}
+
+			//remove from the global namespace
+			unset($db_type, $db_host, $db_port, $db_name, $db_username, $db_password, $db_sslmode, $db_secure, $db_cert_authority);
+		} 
+		else {
+			//save the loaded and parsed conf file to the object
+			$this->configuration = parse_ini_file($this->file);
+		}
+
+	}
+
+	// set project paths if not already defined
+	private function define_project_paths() {
+		// Load the document root
+		$doc_root = $this->get('document.root', '/var/www/fusionpbx');
+		$doc_path = $this->get('document.path', '');
 		//set the server variables and define project path constant
-		$_SERVER["DOCUMENT_ROOT"] = $conf['document.root'];
-		$_SERVER["PROJECT_ROOT"] = $conf['document.root'];
-		$_SERVER["PROJECT_PATH"]  = $conf['project.path'];
-		if (isset($conf['project.path'])) {
-			$_SERVER["PROJECT_ROOT"] = $conf['document.root'].'/'.$conf['project.path'];
-			if (!defined('PROJECT_ROOT')) { define("PROJECT_ROOT", $conf['document.root'].'/'.$conf['project.path']); }
-			if (!defined('PROJECT_PATH')) { define("PROJECT_PATH", $conf['project.path']); }
+		if (!empty($doc_path)) {
+			if (!defined('PROJECT_PATH')) { define("PROJECT_PATH", $doc_path); }
+			if (!defined('PROJECT_ROOT')) { define("PROJECT_ROOT", $doc_root.'/'.$doc_path); }
 		}
 		else {
-			if (!defined('PROJECT_ROOT')) { define("PROJECT_ROOT", $conf['document.root']); }
 			if (!defined('PROJECT_PATH')) { define("PROJECT_PATH", ''); }
+			if (!defined('PROJECT_ROOT')) { define("PROJECT_ROOT", $doc_root); }
 		}
 
-		//add the database settings
-		$this->db_type = $conf['database.0.type'];
-		$this->db_name = $conf['database.0.name'];
-		$this->db_username = $conf['database.0.username'];
-		$this->db_password = $conf['database.0.password'];
-		$this->db_sslmode = $conf['database.0.sslmode'] ?? '';
-		$this->db_secure = $conf['database.0.secure'] ?? '';
-		$this->db_cert_authority = $conf['database.0.db_cert_authority'] ?? '';
-		$this->db_host = $conf['database.0.host'];
-		$this->db_path = $conf['database.0.path'] ?? '';
-		$this->db_port = $conf['database.0.port'];
+		// internal definitions to the framework
+		$_SERVER["PROJECT_PATH"] = PROJECT_PATH;
+		$_SERVER["PROJECT_ROOT"] = PROJECT_ROOT;
 
+		// tell php where the framework is
+		$_SERVER["DOCUMENT_ROOT"] = PROJECT_ROOT;
+
+		// have php search for any libraries in the now defined root
+		set_include_path(PROJECT_ROOT);
 	}
 
 	/**
-	 * Find the path to the config.php
+	 * Find the path to the config.conf file
 	 * @var string $config_path - full path to the config.php file
 	 */
-	public function find() {
+	public static function find(): string {
+		//define the file variable
+		$file = "";
 
 		//find the file
-			if (file_exists("/etc/fusionpbx/config.conf")) {
-				$this->config_path = "/etc/fusionpbx/config.conf";
-			}
-			elseif (file_exists("/usr/local/etc/fusionpbx/config.conf")) {
-				$this->config_path = "/usr/local/etc/fusionpbx/config.conf";
-			}
-			elseif (file_exists($_SERVER["PROJECT_ROOT"]."/resources/config.php")) {
-				$this->config_path = $_SERVER["PROJECT_ROOT"]."/resources/config.php";
-			}
-			elseif (file_exists("/etc/fusionpbx/config.php")) {
-				$this->config_path = "/etc/fusionpbx/config.php";
-			}
-			elseif (file_exists("/usr/local/etc/fusionpbx/config.php")) {
-				$this->config_path = "/usr/local/etc/fusionpbx/config.php";
-			}
-			else {
-				$this->config_path = '';
-			}
-
-		//return the path
-			return $this->config_path;
+		if (file_exists("/etc/fusionpbx/config.conf")) {
+			$file = "/etc/fusionpbx/config.conf";
+		}
+		elseif (file_exists("/usr/local/etc/fusionpbx/config.conf")) {
+			$file = "/usr/local/etc/fusionpbx/config.conf";
+		}
+		elseif (file_exists("/etc/fusionpbx/config.php")) {
+			$file = "/etc/fusionpbx/config.php";
+		}
+		elseif (file_exists("/usr/local/etc/fusionpbx/config.php")) {
+			$file = "/usr/local/etc/fusionpbx/config.php";
+		}
+		elseif (file_exists(dirname(__DIR__, 2) . "/resources/config.php")) {
+			//use the current web directory to find it as a last resort
+			$file = "/var/www/fusionpbx/resources/config.php";
+		}
+		return $file;
 	}
 
 	/**
-	 * Determine whether the config file exists
+	 * Get a configuration value using a key in the configuration file
+	 * @param string|null $key Match key on the left hand side of the '=' in the config file. If $key is null the default value is returned
+	 * @param string $default_value if no matching key is found, then this value will be returned
+	 * @return string returns a value in the config.conf file or an empty string
 	 */
-	public function exists() {
-		$this->find();
-		if (!empty($this->config_path)) {
-			return true;
+	public function get(string $key, string $default_value = ''): string {
+		if (!empty($this->__get($key))) {
+			return $this->__get($key);
 		}
-		else {
-			return false;
+		return $default_value;
+	}
+
+	/**
+	 * Returns the config path or an empty string
+	 * @return string
+	 */
+	public function path(): string {
+		return dirname($this->file);
+	}
+
+	/**
+	 * Returns the file name only of the configuration file
+	 * @return string
+	 */
+	public function filename(): string {
+		return basename($this->file);
+	}
+
+	/**
+	 * Returns the path and the file name
+	 * @return string
+	 */
+	public function path_and_filename(): string {
+		return $this->file;
+	}
+
+	/**
+	 * Returns if the config class has a loaded configuration or not
+	 * @return bool True if configuration has loaded and false if it is empty
+	 */
+	public function is_empty(): bool {
+		return count($this->configuration) === 0;
+	}
+
+	/**
+	 * Returns the array of configuration settings
+	 * @return array
+	 */
+	public function configuration(): array {
+		return $this->configuration;
+	}
+
+	/**
+	 * Ensures the configuration file is loaded only once
+	 * @return config
+	 */
+	public static function load(?string $file = ''): config {
+		if (self::$config === null) {
+			self::$config = new config($file);
 		}
+		return self::$config;
 	}
 }
+
 /*
+//Examples:
+//~~~~~~~~
 $config = new config;
-$config_exists = $config->exists();
-$config_path = $config->find();
-$config->get();
-$db_type = $config->db_type;
-$db_name = $config->db_name;
-$db_username = $config->db_username;
-$db_password = $config->db_password;
-$db_host = $config->db_host;
-$db_path = $config->db_path;
-$db_port = $config->db_port;
-echo "config_path: ".$config_path."\n";
-if ($config_exists) {
-	echo "config_exists: true\n";
-} else {
-	echo "config_exists: false\n";
-}
-echo "db_type: ".$db_type."\n";
-echo "db_name: ".$db_name."\n";
-echo "db_username: ".$db_username."\n";
-echo "db_password: ".$db_password."\n";
-echo "db_host: ".$db_host."\n";
-echo "db_path: ".$db_path."\n";
-echo "db_port: ".$db_port."\n";
-*/
-
-?>
+echo "Config path: " . $config->path() . "\n";
+echo "Config file: " . $config->filename() . "\n";
+echo "Full path and filename: " . $config->path_and_filename() . "\n";
+
+// show old style configuration options
+echo "db_type: ".$config->db_type."\n";
+echo "db_name: ".$config->db_name."\n";
+echo "db_username: ".$config->db_username."\n";
+echo "db_password: ".$config->db_password."\n";
+echo "db_host: ".$config->db_host."\n";
+echo "db_path: ".$config->db_path."\n";
+echo "db_port: ".$config->db_port."\n";
+
+// use current style configuration options even on old config.php
+echo "database.0.type: " . $config->get('database.0.type') . "\n";
+
+// use a default value
+echo "admin.name: " . $config->value('admin.name', 'admin') . "\n";
+
+// get all configuration options by printing the object
+echo "config settings: " . $config . "\n";
+
+// save the configuration options to a file
+file_put_contents('/etc/fusionpbx/config.conf', $config);
+
+// get all configuration options as an array
+var_dump($config->configuration());
+
+//*/

+ 58 - 18
resources/classes/database.php

@@ -256,24 +256,61 @@
 			 */
 			public $message;
 
+			/**
+			 * Config object used to get the database connection params
+			 * @var config
+			 */
+			private $config;
+
+			/**
+			 * SSL Mode used to connect to the database
+			 * @var string prefer or verify-ca. Default is 'prefer'
+			 */
+			public $ssl_mode;
+
+			/**
+			 * Singleton type class
+			 * @var database
+			 */
+			private $database;
+
 			/**
 			 * Called when the object is created
+			 * @param array $params Optional
 			 */
 			public function __construct(array $params = []) {
-				//set the domain_uuid
-				if (isset($params['domain_uuid']) && is_uuid($params['domain_uuid'])) {
-					$this->domain_uuid = $domain_uuid;
+				if (isset($params['config'])) {
+					$config = $params['config'];
 				}
-				elseif (isset($_SESSION['domain_uuid']) && is_uuid($_SESSION['domain_uuid'])) {
-					$this->domain_uuid = $_SESSION['domain_uuid'];
+				else {
+					$config = new config();
 				}
 
-				//set the user_uuid
-				if (isset($params['user_uuid']) && is_uuid($params['user_uuid'])) {
-					$this->user_uuid = $user_uuid;
+				//driver and type point to the same value
+				$this->driver = $config->get('database.0.type', 'pgsql');
+				$this->driver = $config->get('database.0.type', 'pgsql');
+				$this->type = $config->get('database.0.type', 'pgsql');
+				$this->host = $config->get('database.0.host', '127.0.0.1');
+				$this->port = $config->get('database.0.port', '5432');
+				$this->username = $config->get('database.0.username', 'fusionpbx');
+				$this->password = $config->get('database.0.password', 'fusionpbx');
+				$this->db_name = $config->get('database.0.name', 'fusionpbx');
+				$this->db_secure = $config->get('database.0.secure', '');
+				$this->db_cert_authority = $config->get('database.0.cert_authority', '');
+				$this->ssl_mode = $config->get('database.0.ssl_mode', '');
+
+				//save the reference to the single instance of the config to this object
+				$this->config = $config;
+
+				//connect to the database now
+				$this->connect();
+
+				if (!isset($this->domain_uuid) && isset($_SESSION['domain_uuid'])) {
+					$this->domain_uuid = $_SESSION['domain_uuid'];
 				}
-				elseif (isset($_SESSION['user_uuid']) && is_uuid($_SESSION['user_uuid'])) {
-					$this->user_uuid = $_SESSION['user_uuid'];
+				//allow passed domain_uuid in the constructor to override the session domain
+				if (isset($params['domain_uuid'])) {
+					$this->domain_uuid = $params['domain_uuid'];
 				}
 			}
 
@@ -420,9 +457,6 @@
 			 */
 			public function connect() {
 
-				//includes files
-					require dirname(__DIR__, 2) . "/resources/require.php";
-
 				//get the database connection settings
 					//$db_type = $conf['database.0.type'];
 					//$db_host = $conf['database.0.host'];
@@ -523,7 +557,7 @@
 						if (!empty($this->host)) {
 							if (empty($this->port)) { $this->port = "5432"; }
 							if ($this->db_secure === true) {
-								$this->db = new PDO("pgsql:host=$this->host port=$this->port dbname=$this->db_name user=$this->username password=$this->password sslmode=verify-ca sslrootcert=$this->db_cert_authority");
+								$this->db = new PDO("pgsql:host=$this->host port=$this->port dbname=$this->db_name user=$this->username password=$this->password sslmode=$this->ssl_mode sslrootcert=$this->db_cert_authority");
 							}
 							else {
 								$this->db = new PDO("pgsql:host=$this->host port=$this->port dbname=$this->db_name user=$this->username password=$this->password");
@@ -669,8 +703,12 @@
 
 				//if unable to connect to the database
 				if (!$this->db) {
+					$backtrace = debug_backtrace();
 					echo "Connection Failed<br />\n";
 					echo "line number ".__line__."<br />\n";
+					echo "<pre>";
+					print_r($backtrace);
+					echo "</pre>";
 					exit;
 				}
 
@@ -3043,9 +3081,11 @@
 		 * @see database::connect()
 		 */
 		public static function new() {
-			$db = new database();
-			$db->connect();
-			return $db;
+			if (self::$database === null)
+				self::$database = new database();
+				self::$database->connect();
+			}
+			return self::$database;
 		}
 
 		} //class database
@@ -3111,4 +3151,4 @@
 		echo $database->count();
 */
 
-?>
+?>

+ 1 - 2
resources/classes/domains.php

@@ -607,8 +607,7 @@ if (!class_exists('domains')) {
 
 			//get the variables
 				$config = new config;
-				$config_path = $config->find();
-				$config->get();
+				$config_path = $config->config_file;
 
 			//get the list of installed apps from the core and app directories (note: GLOB_BRACE doesn't work on some systems)
 				$config_list_1 = glob($_SERVER["DOCUMENT_ROOT"].PROJECT_PATH."/*/*/app_config.php");

+ 2 - 1
resources/functions.php

@@ -324,7 +324,8 @@
 	if (!function_exists('permission_exists')) {
 
 		function permission_exists($permission_name, $operator = 'or') {
-			$permission = new permissions;
+			$database = database::new();
+			$permission = new permissions($database);
 			return $permission->exists($permission_name);
 		}