getting a variable from a public scope to connect database - php

<?php
$settings['hostname'] = '127.0.0.1';
$settings['username'] = 'root';
$settings['password'] = 'root';
$settings['database'] = 'band';
$settings['dbdriver'] = 'mysql';
/**
* DATABASE
*/
class database
{
protected $settings;
function __construct()
{
}
function connect()
{
$this->start = new PDO(
$this->settings['dbdriver'] . ':host='.
$this->settings['hostname'] . ';dbname='.
$this->settings['database'],
$this->settings['username'],
$this->settings['password'],
array(PDO::ATTR_PERSISTENT => true));
$this->start->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING );
}
}
?>
ok im still a student so today im learning about scope and connections database
the question is how can i put the $settings out of the class in to the protected $settings in class ?

You are already on the right path in the code you show: Don't use public (global) scope at all - it's not regarded good practice in OOP to rely on global variables, because it breaks encapsulation. Instead, inject the settings into the object when initializing it.
You could add a constructor to do that:
function __construct($settings)
{
$this->settings = $settings;
}
and then initialize the class like so:
$database= new database($settings);
or like so, to prevent a variable with sensitive data floating around:
$database= new database(array('hostname' => '127.0.0.1',
'username' => 'root',
'password' => 'root',
'database' => 'band',
'dbdriver' => 'mysql'));
As a side note, in production use, consider deleting the password variable from the array after connecting, for security. It's nothing essential but a nice additional thing to do.

Either pass the $settings as function argument, import it to current scope using global or access via $GLOBALS.
Pass as argument:
public function __construct(array $settings) {
$this->settings = $settings;
}
Import using global:
public function __construct() {
global $settings;
$this->settings = $settings;
}
Use $GLOBALS:
public function __construct() {
$this->settings = $GLOBALS['settings'];
}
I would choose the pass as arg variant. The other versions are only dirty hacks (imho).

A better way would be to either look in a configuration file (a XML file for example) for the configuration, or have the connect() method (or your constructor) called with the desired parameters.

Related

(php) Better way to use global variables? Or how to avoid them?

I was reading somewhere that using global variables is generally a bad idea but I am not sure what the alternative is...
I have this code now and I need to use global $config, $db in every single function. = copy & paste
class Layout {
public static function render($file, $vars) {
global $config, $mysql_db;
mysqli_query($mysql_db, "SELECT * FROM users");
}
}
Is there a better way to do this or do I need to use global keyword or define globals? Because I will need things like mysql connection variables in every function...
Generally speaking, global variables are not good. There are some methods to avoid global variables.
Use a base class
class Base
{
protected $db, $config;
}
class Layout extends Base
{
public void foo()
{
$this->db->query(...);
}
}
Use namespace
namespace MyProject
{
function DB()
{
....
}
}
class Layout
{
public void foo()
{
\MyProject\DB()->query(...);
}
}
Use some frameworks, like Symfony, Laravel, etc (also you can consider some ORM-only frameworks)
The famous frameworks do good job.
Symphony: https://symfony.com/doc/current/doctrine.html
Laravel: https://laravel.com/docs/5.6/database
ORM concept: What is an Object-Relational Mapping Framework?
Static variables in a class is one way to avoid them. Think of static variables in PHP as variables defined on the class rather the instance of the class. The singleton pattern then can grab the variable, or just reference the variable directly on the class. Alternatively, write a __construct method to accept in your incoming variables. Another approach is traits in PHP to help reduce on your copy pasting. Not saying traits are good practice, but they help avoid repeating yourself. Finally, there's almost always a way to abstract over the problem. Having your database connection in a method called render already violates the concept of separation of concerns, and the single responsibility principle. Dependency injection is probably my favorite way to solve the global issue. Find a framework or library that supports it. Lots of good choices out there. Either a framework like Laravel or some composer package that gets you the functionality you need will do.
This is basic example how you can use it
create Database class with file name Database.php
<?php
class Database{
private $con;
function __construct() {
$con = mysqli_connect("localhost","my_user","my_password","my_db");
}
function execute($sql){
// Perform queries, but you should use prepared statemnet of mysqli for
// sql injection
$execute = mysqli_query($con,$sql);
return $execute;
}
}
?>
And let say Render class with name Render.php
<?php
require_once('your_path/Database.php'); // write correct path of file
class Render{
private $db;
function __construct() {
$db = new Database();
}
function test(){
$result = $db->execute('SELECT * FROM users');
}
}
?>
A common pattern is to pass any required classes into the constructor of a class, so this would look more like...
class Layout {
private $config;
private $mysql_db;
public function __construct ( $config, $mysql_db ) {
$this->config = $config;
$this->mysql_db = $mysql_db;
}
public function render($file, $vars) {
$this->mysql_db->query( "SELECT * FROM users");
}
}
This is based around dependency injection (DI) which is common amongst frameworks and allows much more flexibility and control. First link from a search gives - http://php-di.org/doc/understanding-di.html.
Also note that I've changed the call to mysqli_query() to the object oriented interface query(), this requires you create the connection with something like new mysqli("localhost", "my_user", "my_password", "world"), but also makes the interface more consistent (you can change this back if you want)
This would then be created using
$layout = new Layout( $config, $mysql_db);
$layout->render($file, $vars);
This layout is core to a lot of frameworks and is also key when you want to undertake comprehensive testing as you can control the information being passed in and mock classes where needed.
A lot of posters have given beating around the bush answers ... sorry guys ... it's true.
The common way to take care of it is with Dependency Injection. Taking your class
class Layout {
public static function render($file, $vars) {
global $config, $mysql_db;
mysqli_query($mysql_db, "SELECT * FROM users");
}
}
You have a dependency on $config, $mysql_db; Your class depends on them. Typically you would Inject these into the constructor like this.
protected $Mysqli;
protected $config;
public function __construct(array $config, Mysqli $Mysqli){
$this->config = $config;
$this->Mysqli = $mysqli;
}
The issue you have is that the method itself is static, so that bypasses the guarantee that the constructor was called. You can just call the method directly. If possible I would change this to not be static.
But if you insist on keeping it static, then there is a few common ways to solve this, and it depends what the rest of the class looks like. For these I will ignore $config (for the most part) it's not used and it's not clear how it's used or what it's used for (I'm assuming it's for the database). I will also use the Object interface to Mysqli instead of the procedural one.
The most obvious way it to stick them in the method call
public static function render($file, $vars, Mysqli $Mysqli) {
$Mysqli->query("SELECT * FROM users");
}
You can check when the method is called and connect
protected static $Mysqli;
public static function connect(){
//this has the obvious problem of getting the connection info.
$config = require 'config.php';
if(!is_array($config)) throw new Exception('Bad config..'); //etc.
self::$Mysqli = new mysqli(
$config['dbhost'],
$config['dbuser'],
$config['dbpass'],
$config['dbname']
);
}
public static function render($file, $vars) {
if(!$Mysqli) self::connect();
self::$Mysqli->query("SELECT * FROM users");
}
//config.php would look like this
<?php
return [
'dbname' => 'database',
'dbuser' => 'username',
'dbpass' => 'password',
'dbhost' => 'host'
];
This works but it may not be Ideal because now your class is responsible for an external file config.php which if it doesn't exist will cause issues.
You can use a singleton pattern if this class is all static methods. This saves a reference to the class instance (it's self) in a static property. Then when you call getInstance it always returns the same instance, that is way it is called a singleton
class Layout {
//saves a reference to self
private static $instance;
protected $Mysqli;
final private function __construct(Mysqli $Mysqli){
if(!$Mysqli) throw new Exception('Instance of Mysqli required');
$this->Mysqli = $Mysqli;
}
final private function __clone(){}
final private function __wakeup(){}
public static function getInstance(Mysqli $Mysqli = null)
{
if (!self::$instance)
self::$instance = new self($Mysqli);
return self::$instance;
}
public function render($file, $vars) {
self::$Mysqli->query("SELECT * FROM users");
}
}
$Layout = Layout::getInstance();
$Layout->render($file, $vars);
The problem with this is the first call the Mysqli class (or config or whatever) is required, but on subsequent calls it's not. You may or may not know ahead of time that its the first call. But again you could load the config from a file in this example too.
You'll also notice a few methods are Final private this is to prevent overloading and calling them from outside the class.
I also have a trait for Singleton and Multiton (singleton container, multiple Singletons) that I put up on GitHub and Composer, you can find that here
https://github.com/ArtisticPhoenix/Pattern/
Each has it's advantages and disadvantages.
P.S. I just typed these all out on this page, so I didn't test any of them but the theory is sound....
Problems with global variables
you will have trouble naming new global variables, accidentally overriding useful data in your formerly defined global variables
your code might keep unreasonably variables that you no longer need, with all the consequences
There is always an exception from the rule
However, it is better to avoid dogmatic extremism. Sometimes you will need global variables for some reason, but you will need to make sure that you have a very good reason to use global variables.
Alternative
The alternative for using global variables everywhere is to use private or protected class members, initializing the main values via a constructor, like:
class Layout {
private $config;
private $mysql_db;
public function render($file, $vars) {
mysqli_query($mysql_db, "SELECT * FROM users");
}
public function __construct($config, $mysql_db) {
$this->config = $config;
$this->mysql_db = $mysql_db;
}
}
And then you can instantiate Layout via the new keyword and call render of its instance.
Further notes
Also, you can use namespaces so you might have several classes with the same name and I would like to mention local variables as well, inside functions.

Using Variable From Other File To Be Used Inside PHP Class

I have database credential variables from a file, named config.php :
$db_server = 'localhost';
$db_user = 'username';
$db_password = 'secret'
$db_name = 'dbname';
now, I have a PHP class under /class folder and it works perfectly fine for CRUD process. named MysqlCrud.class.php :
class Database {
private $db_host = 'localhost'; // Change as required
private $db_user = 'username'; // Change as required
private $db_pass = 'secret'; // Change as required
private $db_name = 'dbname'; // Change as required
}
but, I want to use centralised variables from config.php. that's why I add some lines like this :
include('../config.php');
class Database {
global $db_server;
global $db_user;
global $db_password;
global $db_name;
private $db_host = $db_server; // Change as required
private $db_user = $db_user; // Change as required
private $db_pass = $db_password; // Change as required
private $db_name = $db_name; // Change as required
}
but, I got this following error message :
Parse error: syntax error, unexpected 'global' (T_GLOBAL), expecting function (T_FUNCTION) in /home/*** on line **
why I can't use variables from config.php file inside Database class? what did I do wrong here? thank you.
The problem with the approach you've chosen to use is that the class is no longer reusable. Any time you instantiate the Database class, it will use the global variables.
I'd be more inclined to set it up like this:
Database.php
class Database {
private $host;
private $db_name;
private $username;
private $password;
function __construct($host, $db_name, $username, $password) {
$this->host = $host;
$this->db_name = $db_name;
$this->username = $username;
$this->password = $password;
}
}
Then in the file you use the Database class:
include('../config.php');
$db = new Database($db_server, $db_name, $db_user, $db_password);
Maybe you can try it like this:
function connDB()
{
$conn=mysql_connect("localhost", "root", "") or die(mysql_error());
mysql_select_db("database") or die(mysql_error());
return $conn;
};
Put this function in your config file or another file such as globalFunctions.php (which contains every general function you need). Just call this function everytime you need it.
Inside a class you can ONLY have member declaration. Global variables are NOT class members, therefore you can't have them inside the class. But you can have them inside methods.
class Database {
private $db_host;
//... the rest of them here
//class constructor. Gets called every time you create an instance of this class.
function __construct() {
global $db_server;
global $db_user;
global $db_password;
global $db_name;
$this->db_host = $db_server;
//.... you get the idea
}
}
Edit 2017-07-11:
Don't do this. Don't use global variables. They are easy to overwrite somewhere and you'd end up debugging a lot. Plus requiring the global keyword is dirty. A proper partial solution is provided by #br3nt. But it still uses global variables and initializes the $db variable in a global scope.
If you have access to the site config in Apache for example, for that website, you can use mod_env to set configuration in environment variables. Example:
<VirtualHost *:80>
.....
SetEnv DB_USER=myDatabaseUser
SetEnv DB_PASSWORD=12345
...
</VietualHost>
And then you can read this in PHP with getEnv('DB_USER') http://php.net/manual/en/function.getenv.php
Another option is to make your config return an array:
config.php
<?php
return [
'db_user'=>'myDbUser,
'db_password'=>'12345'
];
And you should have a single point of entry that ensures read-only access to that config.
Config.class.php
<?php
class Config {
private $_config;
public function __construct() {
$this->_config = include('path/to/config.php');
}
public function __get($key) {
if(isset($this->_config[$key])) {
return $this->_config[$key];
}
return null;
}
}
usage:
$config = new Config();
$dbUser = $config->db_user;
$dbPassword = $config->db_password;
Edit 2, same day
Why are globals bad?
Having global variables is nice because you can access them everywhere, right? Is it also a good practice to have all class members as public? No. Let's say you have a global variable used in many places. Someone accidentally writes this:
if($myGlobalVariable='something') { ... }
And his code works with just a weird bug that nobody cares about. But your code breaks because you actually depend on the exact value of $myGlobalVariable. And you look at the config, and see that it's the right value, and then scratch your head.
This is just one case. Unhindered read+write access to shared resources can be dangerous. It's easy to override, and no error will be output. It also pollutes the global space.
It's also a code smell if you have global functions in a config file. Think of config files as static text files that shouldn't even contain code. The only reason they're PHP files is because of speed, ease, and harder to break security that way.
Here is an option to set your class members to a global value using __construct()
include('../config.php');
class Foo {
private $db_host;
private $db_user;
private $db_pass;
private $db_name;
public function __construct() {
global $db_server;
global $db_user;
global $db_password;
global $db_name;
$this->db_host = &$db_server; // Change as required
$this->db_user = &$db_user; // Change as required
$this->db_pass = &$db_password; // Change as required
$this->db_name = &$db_name; // Change as required
}
}
Since we are using Assignment by Reference the inner class members are the same variables as the global ones (thus change as required).
Also if you want to avoid writing global you can use the reserved variable $GLOBALS, which as described in the documentation is:
An associative array containing references to all variables which are currently defined in the global scope of the script. The variable names are the keys of the array.
So your code could became something like this:
$GLOBALS = array(
'DB_HOST' => 'localhost',
'DB_USER' => 'user',
'DB_PASS' => 'secret',
'DB_NAME' => 'my_db'
);
class Foo {
private $db_host;
private $db_user;
private $db_pass;
private $db_name;
public function __construct() {
$this->db_host = &$GLOBALS['DB_HOST']; // Change as required
$this->db_user = &$GLOBALS['DB_USER']; // Change as required
$this->db_pass = &$GLOBALS['DB_PASS']; // Change as required
$this->db_name = &$GLOBALS['DB_NAME']; // Change as required
}
}

OOP PDO Global Variables within Class

I am looking into oop to enhance my web dev knowledge. At the moment i am having a bit of an issue. I have created a Database class that contains all the queries etc. ( fetch, count, etc. ) This just allows the queries or updates to take up less space in the other classes etc. The issue i am having is passing this class along and making it globally accessible. I have used
global $db;
within a class function but i read that it is bad practice to use that. I also do not want to pass the $db variable as a parameter if i did i would have to change a lot of my current classes and it would just be easier if i can make $db globally available in a "good" practice way.
I can provide my Database class if necessary it is just a simple class with the variable that initiates the connection through construct.
( Second Question )
I was also reading about the singleton instance function, before implementing i read that it was also considered bad practice. Is there something that should take its place?
( I decided to place the class below )
class Database {
private $host = 'localhost';
private $user = 'xxx';
private $pass = 'xxxx';
private $dbname = 'xxxxx';
private $dbh;
private $error;
public function __construct(){
// Set DSN
$dsn = 'mysql:host=' . $this->host . ';dbname=' . $this->dbname;
// Set options
$options = array(
PDO::ATTR_ERRMODE => PDO::ERRMODE_WARNING,
PDO::ATTR_EMULATE_PREPARES => false
);
// Create PDO Instance
$this->dbh = new PDO($dsn, $this->user, $this->pass, $options);
}
public function fetch($sql, $param = "") {
$this->stmt = $this->dbh->prepare($sql);
if (empty($param)) {
$this->stmt->execute();
} else {
$this->stmt->execute($param);
}
return $this->stmt->fetch();
}
}
$db = new Database();
An example of what i am attempting to accomplish is as follows
( User Profile Page )
class User {
function set_user($input) {
if (is_numeric($input)) {
$this->user = $input;
} else {
$user = $db->fetch("SELECT userid FROM users WHERE url=:url", array(':url' => $input));
$this->user = $user['userid'];
}
}
}
AN issue with your approach is not the global var, but that the DB connection is permanently open. It is not always good. This is kind of service object and it is not partiularily bad to have it globally visible.
If you don't want it global and want to solve the open connection issue, you can easily close the connection in the destructor, remove the global var and create/use/delete the DB wherever required.
Another approach would be not to use singleton, but so called service class. Make DB class not instanciable, but rather define service methods (all static) - open, close, execute, or whatever. Than the DB clients should not create DB object, but only access the static methods. This approach fits the reality very nice, as DB-access is seen as a service.

Setting MySQL session variables using PDO()

I am using PHP's PDO to access my MySQL DB. I typically create a singleton class for my connection. I would like to set a MySQL session variable when initiating the connection, and later on as well. See below for my failed attempts to set MySession1 and MySession2. How can I do this? Thanks
EDIT. Just found out that it works if you only use one #. I was originally using two at-signs ## which I thought the documentation at http://dev.mysql.com/doc/refman/5.0/en/using-system-variables.html required. How am I misinterpreting the documentation?
class db {
private static $instance = NULL;
private function __construct() {} //Make private
private function __clone(){} //Make private
public static function db() //Get instance of DB
{
if (!self::$instance)
{
self::$instance = new PDO("mysql:host=localhost;dbname=myDB", 'myUsername', 'myPassword');
self::$instance->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
self::$instance->exec('SET #MySession1=123');
}
return self::$instance;
}
}
db::db()->exec('SET #MySession2=321');
$stmt=db::db()->query('SELECT #MySession1 AS MySession1, #MySession2 AS MySession2');
$rs=$stmt->fetch(PDO::FETCH_ASSOC);
echo("MySession1={$rs['MySession1']} MySession2={$rs['MySession2']}");
There is a difference between server variables and user defined variables. See http://dev.mysql.com/doc/refman/5.0/en/user-variables.html for the description of user defined variables.

PHP Fatal Error: Cannot use object of type DataAccess as array

I cannot figure out why I am getting the following error in PHP:
Fatal error: Cannot use object of type DataAccess as array in /filename on line 16.
Here is the relevant code for the file:
class StandardContext implements IStandardContext
{
private $dataAccess;
// (CON|DE)STRUCTORS
function __construct($config)
{
$this->dataAccess = new DataAccess($config['db']); //this is line 16
}
$config refers to the following:
$config = require(dirname(__FILE__)./*truncated*/.'Config.php');
Here is the relevant code for Config.php:
return array(
// Database connection parameters
'db' => array(
'host' => 'localhost',
'name' => 'visum',
'user' => 'root',
'password' => ''
)
);
Here is the relevant code for the DataAccess object:
class DataAccess
{
private $link;
private $db;
function __construct($dbConfig)
{
$this->link = mysql_connect( $dbConfig['host'], $dbConfig['user'], $dbConfig['password'] ) or die(mysql_error());
$this->db = $dbConfig['name'];
mysql_select_db($this->db) or die(mysql_error());
}
Any help would be greatly appreciate, I am fairly new to PHP and am absolutely stumped.
Edit: BTW, I have included the following code to test StandardContext, which actually works (ie. it allows me to make changes to my database farther down than I have shown)
class StandardContext_index_returns_defined_list implements ITest
{
private $dataAccess;
function __construct($config)
{
$this->dataAccess = new DataAccess($config['db']);
}
It's almost like you are trying to use a singleton pattern, but for every StandardContext object you instantiate, you are passing in database parameters (via $config array). I think what's happening is that you are passing the $config array more than once, after the first pass the $config is no longer an array, but an instance of the DataAccess class, which is why you are getting that error. You can try the following:
class StandardContext implements IStandardContext
{
private $dataAccess;
// (CON|DE)STRUCTORS
function __construct($config)
{
if ($config instanceof DataAccess) {
$this->dataAccess = $config;
} elseif ((is_array($config)) && (array_key_exists('db', $config))) {
$this->dataAccess = new DataAccess($config['db']);
} else {
throw new Exception('Unable to initialize $this->dataAccess');
}
}
this is problem with your
private $dataAccess;
check the array object here
http://www.php.net/manual/en/class.arrayobject.php
whenever you declare outside a method inside class, it will consider as Object , so you have to declare inside method or declare as method itself else remove implements from your class.
your $dataAccess is an Object , because you declare it outside the method and your new DataAccess($config['db']) will return an arrayObject because you implements that, so it is trying to convert from Object to arrayObject leads an error

Categories