I am converting an existing web site from procedural PHP code to OOP. I'd like to set a Permissions class that contains information, such as $USER data, relevant to multiple extended classes (i.e. Database, Login, Validation, etc).
Am I able to set class instance variables for child classes (i.e. Database, Login, Validation) in the parent class (i.e. Permissions) via the magic method __construct(), and have them appear in each child class except the class they reference?
i.e. 'class Database extends Permissions' does not include Permissions::$db, otherwise a loop and error occurs.
Example code snippets appear below :
<?php
class Permissions {
protected $db = '';
protected $login = '';
protected $val = '';
protected $USER = array (); // i.e. $this->USER['id'], $this->USER['access-level']
public function __construct() {
$this->db = new Database();
$this->login = new Login();
$this->val = new Validation();
// Code to populate $this->USER
}
}
class Database extends Permissions {
// Code
}
class Login extends Permissions {
// Code
}
class Validation extends Permissions {
// Code
}
?>
Is it better for each child class to call upon only the classes it needs to access? i.e.
<?php
class Permissions {
protected $USER = array (); // i.e. $this->USER['id'], $this->USER['access-level']
public function __construct() {
// Code to populate $this->USER
}
}
class Database extends Permissions {
// Code
}
class Login extends Permissions {
private $db = '';
public function __construct() {
$db = new Database();
}
// Code
}
class Validation extends Permissions {
public function __construct() {
$db = new Database();
}
// Code
}
?>
Related
I have 2 files.
One is index.php, which checks for a user login
In this file i create a dbManager class which handles a database.
If dbManager can validate login data, i forward the user to lobby.php (via header).
In lobby.php, i create a Manager class which manages the lobby.
I would like to be able to use the Manager class to forward a query to the DB Manager like this:
index.php
$dbManager = new dbManager();
$userid = $dbManager->validateLogin(($_POST["name"], $_POST["pass"];
if ($userid){
$_SESSION["userid"] = $userid;
header("Location: lobby.php");
}
lobby.php
session_start();
if (isset($_SESSION["userid"])){
$manager = new Manager($_SESSION["userid"]);
$manager->getGames();
}
Class dbManager {
things
}
Class Manager {
public userid;
function __construct($id){
$this->userid = $id;
}
function getGames(){
$ret = $dbManager->queryDB($this->userid);
}
}
I am getting the following notices:
Notice: Undefined variable: dbManager in D:\SecureWAMP_Portable\htdocs\projectX\gameManager.php on line 11
and
Fatal error: Call to a member function getGamesForPlayer() on a non-object in D:\SecureWAMP_Portable\htdocs\projectX\gameManager.php on line 11
What am i doing wrong ?
You should define each class in a new file and include those files (good practice is using autoloading). If you only require one object of a class, you should take a look at the Singleton pattern, as you will always get the same class instance.
For that you define a static protected variable which will hold our class instance and use a static public method to return the class instance and if necessary, create a new class instance. We will define the constructor of each class as private, so the static public method has to be used.
If you have arguments you pass to the constructor, define them for the static public method too and pass the arguments to the constructor in the static public method.
The file DBManager.php will define the class DBManager and will use singleton pattern, as this class will handle connections to the database.
Class DBManager {
static protected $instance = NULL
private __construct() {
//Write the code you want to execute when a new class instance is requested
self::$instance = &$this; //Put a reference to this instance in our static variable
}
//Our static public method to retrieve a class instance
static public app() {
if(self::$instance === NULL OR !is_a(self::$instance, 'DBManager')) { //With is_a we are making sure our self::$instance variable holds a class instance of DBManager
$instance = new DBManager();
}
return self::$instance;
}
/* All your other methods... */
}
Our Manager.php will hold the class Manager and to retrieve a class instance of DBManager we will call our static public method app(). It is valid to also make the class Manager singleton, but only if always only one class instance should exist.
Class Manager {
public userid;
function __construct($id){
$this->userid = $id;
}
function getGames() {
$ret = DBManager::app()->queryDB($this->userid);
}
}
Now our basic index.php just instead of using new DBManager() we will use the static method to return a class instance.
require_once 'DBManager.php';
$dbManager = DBManager::app();
$userid = $dbManager->validateLogin($_POST['name'], $_POST['pass']);
if($userid) {
$_SESSION['userid'] = $userid;
header("Location: lobby.php");
}
In our lobby.php we will use the new Manager class for the first time.
session_start();
require_once 'DBManager.php';
require_once 'Manager.php';
$dbManager = DBManager::app();
if(isset($_SESSION['userid'])) {
$manager = new Manager($_SESSION['userid']);
$manager->getGames();
}
you should not use global vars because it is bad style and will make your life really hard when the application gets bigger.
In your case you want to use the dbManager in different classes.
Since db transaction are likely to be used often, look at the singleton pattern, so there can be only one instance of this class.
But be aware that you should keep the number of singletons as low as possible.
Look into this page to see how the singleton pattern can be implemented in PHP:
http://www.phptherightway.com/pages/Design-Patterns.html
You seem to have a misconception about how php code is distributed and used over multiple files and how these files are used.
For example if you use include() or require() instead of header(), you can refer to the data you have defined so far.
A more advanced approach is to use __autoload() or [spl_autoload_register()](http://php.net/manual/de/function.spl-autoload-register.php) to load classes into your script and have an index.php that starts your script.
(Also read the link #blacksheep_2011 provided)
A good practice is to declare each class in separate file. After that, you need to include the needed class into a file in which you are planning to use that class functionality.
Create file DBManager.php:
Class DBManager {
things
}
Create file Manager.php which will contain Manager class declaration:
include('DBManager.php');
Class Manager {
public userid;
function __construct($id){
$this->userid = $id;
}
function getGames(){
$dbManager = new DBManager();
$ret = $dbManager->queryDB($this->userid);
}
}
Include your classes where they are needed:
index.php
require_once 'DBManager.php';
$dbManager = new DBManager();
$userid = $dbManager->validateLogin(($_POST["name"], $_POST["pass"];
if ($userid){
$_SESSION["userid"] = $userid;
header("Location: lobby.php");
}
lobby.php
session_start();
require_once 'Manager.php';
if (isset($_SESSION["userid"])){
$manager = new Manager($_SESSION["userid"]);
$manager->getGames();
}
I'm trying to learn how to properly code PHP OOP.
This is where I'm running into issues.
I created several classes that extend main Application class and I want to make things work properly.
I have main file that's index.php that looks like this:
include_once('classes/Application.php');
include_once('classes/Configuration.php');
include_once('classes/Database.php');
$app = new Application;
$config = new Configuration;
$db = new Database;
var_dump($app->db_connected);
var_dump($db->db_connected);
$db->connect($config->dbhost, $config->dbuser, $config->dbpass, $config->dbname);
var_dump($app->db_connected);
var_dump($db->db_connected);
The output is:
1. bool(false)
2. bool(false)
3. bool(false)
4. bool(true)
My main application file looks like this:
class Application {
public $db_connected = false;
}
And my Database class looks like this:
class Database extends Application {
function connect($dbhost, $dbuser, $dbpass, $dbname) {
if(!$this->db_connected) {
mysql_connect($dbhost, $dbuser, $dbpass) or die(mysql_error());
mysql_select_db($dbname) or die(mysql_error());
$this->db_connected = true;
}
}
}
So the question is, why would line #3 of the output of index.php display false? The db_connected property has been overridden in Database class and set to TRUE, but it still returns false.
Although when accessed directly from Database class instance it shows TRUE correctly. What's the deal here?
Also when does the class EXTEND command occurs? Whenever parent class' instance is created or I have to manually create instance of the child class?
It seems you are reaching for the concept of of a static variable all instances of a class share the same static variable so using the new twice will not be an issue.
You can see the code on ideaone.
// your code goes here
class Application {
static $db_connected = false;
}
class Database extends Application {
function connect() {
if(!static::$db_connected) {
static::$db_connected = true;
}
}
}
$app = new Application;
$db = new Database;
var_dump(Application::$db_connected);
var_dump(Database::$db_connected);
$db->connect();
var_dump(Application::$db_connected);
var_dump(Database::$db_connected);
Your comment make me think you are looking for a better pattern all together. I would like to throw out some key principles namely OCP and LSP SOLID.
In this case you would avoid having Application being an instance of Database but instead use dependency injection. Here is the refactored code.
class Database {
private $db_connect = false;
public function connect () {
if(!$this->db_connect) { /* do connection */ }
}
}
class Application {
private $db;
public function setDatabse(Database $db) {
$this->db = $db;
}
public function getDatabase() {
return $this->db;
}
}
$db = new Database;
$app = new Application;
$app->setDatabase($db);
$app->getDatabase()->connect();
This line is your hint
Although when accessed directly from Database class instance it shows TRUE correctly. What's the deal here?
You have 2 instances. Above you are checking $db instance which you connected with, and then you print from $app which was never connected. They are separate entities, one is connected one is not.
Extend occurs as soon as the file is loaded, read by the php interpreter, this happens regardless of ever using the class.
Extend is called from the child and inherits everything form the class it extends. So if you call a child method in the parent, well you are doing it backwards. It goes one way, Prent -> Child.
I would use Dependance injection for the database, then you can reuse it's code.
Like this:
//parent class
class Application {
//holds a reference to the Database class
protected static $db_conn = false;
public function __construct($db){
self::$db_conn = $db;
}
}
//child class of Application
class Application2 extends Application {
public function getSomething($id){
return self::$db_conn->getbyId($id) ;
}
}
//separate utility class
class Database{
static $conn;
public function __construct( $dbhost, $dbname, $dbuser, $dbpass, $dbname) {
static::$conn = mysqli_connect($dbhost, $dbuser,$dbpass,$dbname);
}
public function getbyId( $id ){
..code to get stuff by id using $conn - previous connection ...
return $result;
}
}
$db = new Database("myhost", "myuser", "mypassw", "mybd");
$app = new Application2( $db );
$app->getSomething(1);
//create another app with the same database connection, this is the value of injecting it.
$second_app = new Application2( $db );
See you can reuse database over and over, you can replace it without changing the code in Application as long as the calls for the functions of the Database class don't change. Each thing is responsible for it's own business.
This is called separation of concerns.
Inheritance is good, when it's needed. You might have an basic application for free users of you're services and then extend that with a premium application for paid members. Sense they paid they get all the free functionality, but also the premium stuff to.
In my example above the database is something they both need, as well as other things will probably use this. Such as a login system may need a database connection, payment system might, a shopping cart might. These are all separate objects, they don't / nor should they extend off of one Master Class, that's a bad idea. Keep them separate.
STATIC
I seen mention of the :: static object operator. My example is a bit flawed when using the static property protected static $db_conn = false;
$app = new Application2( $db );
$second_app = new Application2( $db ); //assigning db 2x is not needed.
The reason for :: and the -> normal way. Is that static :: is shared across all instance of a class, and -> is just this instance of the class. I had assigned the $db class to a static variable 2 times a better way would have been like this.
//parent class
class Application {
protected static $db_conn = false;
//separate method then construct.
public function connect($db){
self::$db_conn = $db;
}
}
//we'll keep the rest of the code the same here.
$db = new Database();
$app = new Application2();
$app->connect( $db );
$second_app = new Application2();
$second_app->getSomething(1);
Now in this example $second_app never ran it's connect method. But because the first $app did and because the static for the database variable protected static $db_conn. Now all classes that have extended the Application class have a database connection. This is what static does. It's value is shared across all instance of the class. So when you see :: think all class instance and when you see -> think only this class instance. It's actually one thing I love about php, makes it so much easier to keep track of then in some other languages.
Not to confuse you but the other use of the :: is not actually needing an instance at all. Assume you have a Config class like this.
class Config{
static $db = 'hello';
static $items = array('one' => 'item 1' );
private __construct(){} // no construction allowed
static function getItem( $which ){
return self::$items[$which];
}
}
Now without ever creating an instance of the class by calling new Config() , you can simply.
echo Config::$db;
// prints hello
echo Config::getItem('one');
// prints 'item 1'
This is quite use full for config type classes. Where they are an empty shell just used to store data in and you don't need an object for them, essentially a way to keep things organized. So tying this in to the previous examples
$db = new Database(Config::$myhost, Config::$myuser, Config::$mypassw, Config::$mybd);
In your case best OOP practice is to use Mediator pattern. Concrete Mediator will be Application class:
class ApplicationBase {
private $db;
private $cfg;
public function setDb(Database $db) {
$this->db = $db; return $this;
}
public function setConfig(Config $cfg) {
$this->cfg = $cfg; return $this;
}
}
class Application extends ApplicationBase {
public function getDsn() {
return $this->cfg->getDsn();
}
public function getDbUser() {
return $this->cfg->getDbUser();
}
public function getDbPass() {
return $this->cfg->getDbPass();
}
public function getConnection() {
return $this->db->getConnection();
}
}
class AppComponent {
protected $app;
public function __construct(Application $app) {
$this->app = $app;
}
}
class Config extends AppComponent {
private $dsn;
private $dbuser;
private $dbpass;
// ... getters and setters
}
class Database extends AppComponent {
private $connection;
private function connect() {
$this->connection = new PDO(
$this->app->getDsn(),
$this->app->getUser(),
$this->app->getPass()
);
}
public function getConnection() {
if (null === $this->connection) $this->connect();
return $this->connection;
}
}
class Model extends AppComponent {
protected $table;
// Model stuff here
}
class Content extends Model {
public function getNews() {
$db = $this->app->getConnection();
return $db->query("SELECT * FROM $this->table LIMIT 5")->fetchAll();
}
}
Such architecture will be enough for simple, clean-looking applications and classes will be ready for easy unit-testing:
$app = new Application();
$cfg = new Config($app);
$db = new Database($app);
$app->setDb($db)->setConfig($cfg);
$content = new Content($app);
$news = $content->getNews();
I'm playing around with OOP in PHP and I've got the following code:
index.php:
<?php
include('user.class.php');
include('page.class.php');
$user = new User;
$page = new Page;
$page->print_username();
?>
user.class.php:
<?php
class User {
public function __construct() {
$this->username = "Anna";
}
}
?>
page.class.php:
<?php
class Page extends User {
public function __construct() {
}
public function print_username() {
echo $user->username;
}
}
?>
My problem occurs in the class "Page", in the print_username() function.
How do I access the $user object's properties within this class? I am, as you can see, defining the two objects in index.php.
Thanks in advance
/C
class User {
public $username = null;
public function __construct() {
$this->username = "Anna";
}
}
class Page extends User {
public function __construct() {
// if you define a function present in the parent also (even __construct())
// forward call to the parent (unless you have a VALID reason not to)
parent::__construct();
}
public function print_username() {
// use $this to access self and parent properties
// only parent's public and protected ones are accessible
echo $this->username;
}
}
$page = new Page;
$page->print_username();
$user should be $this.
class User {
public $username = null;
public function __construct() {
$this->username = "Anna";
}
}
class Page extends User {
public function print_username() {
echo $this->username; //get my name! ($this === me)
}
}
I see some confusion here:
You've caused your Page class to inherit from User. This means that the page itself has all the properties of the User class, and in fact, could be used in place of a User class. As the print_username() method is written in your code, it won't work - because it doesn't have a reference to a $user variable. You could change $user to $this to get a username in the print_username() method, and borrow from the parent class (User) to get the username property.
My thinking is though, that you didn't intend to do that. A Page is not a User, after all - they aren't related to each other. So what I'd do instead is remove extends User from the Page class. This will make a Page a Page, and a User a User.
But how is the Page going to print a username? Pages need to do that of course. What you could do instead is pass the $user object as a parameter to the Page's __construct() method, and then you can reference that value in your Page.
The first way of writing the code, using extends, involves inheritance. The second way of writing the code when passing in the user as a parameter involves composition. In a situation like this, with two separate ideas (Pages and Users), I would use composition to share and access object properties instead of inheritance.
I would do this instead:
<?php
class User {
public function __construct() {
$this->username = "Anna";
}
}
class Page {
private $user;
public function __construct($user) {
$this->user = $user;
}
public function print_username() {
echo $user->username;
}
}
$user = new User;
$page = new Page($user);
$page->print_username();
?>
I have an issue accessing top level variables from sub-level class.
Here's an example...
Application.php:
class Application {
var $config;
var $db;
function __construct() {
include_once('Configuration.php');
include_once('Database.php');
$this->config = new Configuration;
$this->db = new Database;
}
}
Configuration.php:
class Configuration {
var $dbhost = 'localhost';
}
Database.php:
class Database {
function __construct() {
echo parent::config->dbhost;
}
}
It is clear to me that usage of parent is wrong here as the subclass does not extend the parent class, but how do I access it?
Thank you.
You should create a Base class that in its construct creates a $db link. Then let all classes that require database access extend that class. Your nomenclature here with "parent class" is incorrect.
class Base {
private $db; // Make it read-only
function __construct() {
$this->db = DB::connect(); // It's a good practice making this method static
}
function __get($property) {
return $this->$property;
}
}
class Application {
public $config;
function __construct() {
parent::__construct();
require_once 'Configuration.php';
require_once 'Database.php';
$this->config = new Configuration();
}
function random_function() {
$this->db(....) // Has full access to the $db link
}
}
The parent notation is used to access the parent of the object in the object hierarchy. What you are doing here is trying to get at the caller, not the parent
The way that you would do this is to pass in an instance of your configuration to the database object.
class Database {
protected $config;
public function __construct(Configuration $config){
$this->config = $config;
}
public function connect(){
//use properties like $this->config->username to establish your connection.
}
}
The parent notation is used when you extend a class and make a child class to call methods on the parent .
class MySuperCoolDatabase extends Database {
protected $is_awesome;
public function __construct(Configuration $config){
// do all the normal database config stuff
parent::__construct($config);
// make it awesome
$this->is_awesome = true;
}
}
This defines a child class, which is a type definition that serves the same role as the base class with a slightly different implementation. Instances of this can still be said to be a Database.... just a different kinds of database.
Well, although I think that Orangepills answer is better. If you dont want to use it and since all variables are public, you could simply pass the variable like this:
class Application {
var $config;
var $db;
function __construct() {
include_once('Configuration.php');
include_once('Database.php');
$this->config = new Configuration;
$this->db = new Database($this->config->dbhost);
}
}
class Configuration {
var $dbhost = 'localhost';
}
class Database {
function __construct($dbhost) {
echo $dbhost;
}
}
I've 3 class (Mysqliconn, Users and News).
Mysqliconn.class
Class Mysqliconn {
..connect to db
}
News.class
class News {
private $db;
public function __construct( Mysqliconn $db ) {
$this->db = $db;
}
public test() {
do something...
}
}
Users.class
class Users {
private $db;
public function __construct( Mysqliconn $db ) {
$this->db = $db;
}
public test2() {
do something...
}
}
In my php page
$db = new Mysqliconn();
$nw = new News( $db );
$us = new Users( $db )
$us->test2();
$nw->test();
My error
Catchable fatal error: Argument 1
passed to Users::__construct() must be an instance of Mysqliconn,
none given, called in ....\class\class.news.php
Now in my class Users I would like to to call a News class method,
but I get an error if I try to istantiate the class News inside class Users.
How can I do this?
Thanks.
Plz post what error you are getting. Otherwise it's difficult to capture the problem. But if you dont want to instantiate the news class in the user class you can access it like
class News
{
// news class
}
class Users
{
public function some_method(News $news){
// work with the $news object
}
}
$us = new Users();
$us->some_method(new News(/*$db*/));
This is just a basic code to give you an idea of how you can use it. In a production environment you have to take a lot of precautions.