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();
?>
Related
is possible to connect two class like this ?
we want first login with admin class then use $user without passing variables
class app {
$user;
}
class admin extends app {
function login() {
$this->user = "lamar";
}
}
class song extends app {
function add() {
// add somthing with $this->user
}
}
$admin = new admin();
$admin->login();
now how to use class song to add song with user "lamar" ?
Connecting classes and use their values without passing variables is not possible. Have a look at dependency injection to get access to a class within another.
Regarding to your example it could look like this:
class User {
private $username;
function getUsername() {
return $this->username;
}
}
class Song {
private $user;
function __construct(User $user) {
$this->user = $user;
}
function getUser() {
return $user;
}
}
It might be a good idea to optimize your approach for app, admin and song otherwise your architecture could become quite complicated.
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
}
?>
I am working with a plugin where there is a protected function like so
<?php
class CustomUploadHandler extends UploadHandler {
protected function get_user_id() {
//If I manually enter a value here, the value passes along
return ('myvariable');
}
}
?>
Yet when I make a variable like
<?php $myvar = 'myvariable'; ?>
and try to insert it into the function like this
<?php
class CustomUploadHandler extends UploadHandler {
protected function get_user_id() {
//If I use a variable, the value is lost
return ($myvar);
}
}
?>
it completely fails...
I am unfamiliar with protected classes and also how return() works so any help would be greatly appreciated.
I have tried many lines of code such as
print $myvar; return $myvar; echo $myvar; with and without ()
Don't introduce global state via the global keyword. You'll be welcoming a world of pain down the line.
Instead, inject the dependency (the value, a user ID in this case) into the class when it's created, or with a setter.
class CustomUploadHandler extends UploadHandler
{
private $user_id;
protected function get_user_id()
{
return $this->user_id;
}
// setter injection; the value is
// passed via the method when called
// at any time
public function set_user_id($user_id)
{
$this->user_id = $user_id;
}
// constructor injection; the value is
// passed via the constructor when a new
// instance is created
public function __construct($user_id)
{
$this->set_user_id($user_id);
}
}
Then when you have an instance of this class:
// creates and sets $user_id = 42
$customUploadHandler = new CustomUploadHandler(42);
// sets $user_id = 77
$customUploadHandler->set_user_id(77);
Protected means that only the class itself, the parent class and the childs of the class where the function is defined can use the function. So it depends from where you call the function for it to work. return statements without brackets should be fine.
After restructuring my entire class layout, I'm still having trouble using multiple class instances.
class User {
public $variable;
public function getUser() {
$this->variable = 'It works!';
return 'bob';
}
}
class Base {}
class One extends Base {
public function test() {
print_r($User->variable); // Want it to print 'It works!'
}
}
$User = new User();
$username = $User->getUser();
if($username === 'bob') {
$One = new One();
$One->test(); // prints "Notice: Undefined property: One::$variable
}
What the above code does: The class User gets the username. If the username is bob, it will create an object one and try to print the variable from class User. In my real code, User is extremely intricate and passing a bunch of things with __construct just isn't what I'm looking for.
I think the solution (which is why I titled this question as so) would be to create a new class User within class Base but I just can't wrap my head around having to create a whole new object and re-initiate everything just to get the username.
Inject your User instance:
class One {
private $user;
public function __construct(User $user) {
$this->user = $user;
}
public function test() {
print_r($this->user->variable);
}
}
$User = new User();
$One = new One($user);
$One->test();
This doesn't have any undue effects with regards to efficiency, as the instance is passed by reference as opposed to copied.
The class One does not know about the $User variable and therefore can not access it.
You could either pass the variable to the class in the constructor or set it after creation of an object of the class.
class One extends Base {
protected $user;
public function setUser(User $user) {
$this->user = $user;
}
public function One(User $user) {
$this->setUser($user);
}
public function test() {
print_r($this->user->variable);
}
}
Or (but please don't, it is bad practice) set the $User variable to global.
global $User
Below is a very basic example of the trouble I am having.
Now why must I use $session = new Session(); in the database.class.php file when in my system the files are included in a way that it would be visible already.
In other words I cannot call $session = new Session(); outside of any other classes, to make it work in other classes I have to call create a new objectof the session class in every class I want to use it in, how can I avoid this and make it work without doing that?
// Session.class.php
class Session{
public function __construct()
{
session_start();
}
public function set($name, $value)
{
$_SESSION[$name] = $value;
}
}
...
// Database.class.php
class Database{
public static function mysql_query_2($query)
{
if ($session->get('user_role') == 10){
$_SESSION['queries']++;
}
return mysql_query($query);
}
}
First and foremost, your Session class should be a singleton - the reason is that multiple calls to session_start() will get you nothing but problems (session id gets regenerated)
So...
/**
* Session singleton class
*/
class Session
{
// prevents object instanciation ($obj = new Session())
protected function __construct() {
session_start();
}
// prevents object cloning
protected function __clone() {}
public static function getInstance()
{
static $instance = null;
if ($instance == null)
$instance = new Session();
return $instance;
}
// rest of the code
}
To use it you simply call:
$sess = Session::getInstance();
$sess->doSomething();
or simply
Session::getInstance()->doSomething();
Anywhere you need to use the session class.
Update:
- I see you probably don't grasp the concept of singletons yet, so here's a proper wikipedia link 8)
If I understand your problem correctly, one solution to your problem is to use a Singleton.
final class Singleton {
protected static $_instance;
// Singleton can't be constructed or cloned
protected function __construct(){ }
protected function __clone() { }
// Create an instance if necessary and return an instance.
public static function getInstance(){
if( self::$_instance === NULL ) {
session_start();
self::$_instance = new self();
}
return self::$_instance;
}
public function set($name, $value){
$_SESSION[$name] = $value;
}
}
In index.php you would have:
$session = Session::getInstance();
$session->get();
Then in your Database class you would have:
// Database.class.php
class Database{
public static function mysql_query_2($query)
{
if (Session::getInstance()->get('user_role') == 10){
$_SESSION['queries']++;
}
return mysql_query($query);
}
}
You would only ever create one instance of the Session class (which is what I think your looking for). Then when you need the Session class somewhere else (ie your Database class) you just call Session::getInstance() again. There is only one instance of Session ever created and you can use it globally across your script.