PHP Keep parent class properties when using a child class constructor - php

I have a parent class Database as below
class Database{
/** #var Object Filemaker object across the application*/
protected $fm = null;
public function __construct(){
if($this->fm == null){
$this->_openConnection();
}
}
/**
* Function to open connection to FileMaker
* #return null
*/
private function _openConnection(){
$this->fm = new FileMaker();
}
}
and one of my child class is
class Login extends Database{
public function validate(){
// $this->fm is accessed here with no constructors
print_r($this->fm);
}
}
I'm creating object by,
$login = new Login();
$login->validate();
Note that in this Login class, I don't have any constructor. So at the line $login = new Login(); it is calling the Database class constructor and it calls the _openConnection(). This is fine
This is my another child class:
class PouleManipulation extends Database{
private $year;
public function __construct(){
// $this->fm is only accessed if I call parent class constructor
parent::__construct();
$year = '2015';
}
public function processQueue(){
$this->fm->perform();
}
}
and
$pm = new PouleManipulation();
$pm->processQueue();
Now, when I call the $pm = new PouleManipulation();, note that I have a constructor for this class. If I access $this->fm, then it is throwing an undefined error. I googled and found that,
Inside this class constructor I need to use parent::__construct(); to access the parent class properties. If I didn't use parent::__construct(); then the parent class object is not accessible when you are having a constructor in child class
Now, the problem is in the Database class $fm is null and it again calls the _openConnection(). I have totally 10 classes extending Database class and whichever classes having the constructor it calls the _openConnection().
I need the $this->fm to be accessed across all the child classes with calling the _openConnection() only one first time and need the $this->fm in all classes well. how do you achieve this?

Let's clarify a bit. The properties are set on an object level. This means that everytime you instantiate a class creating an object, this object is new and has its own properties different from the others you instantiated (they may have the same value, but they're stored in different places and they're independent).
Example:
class Database {
public $fm = "Value";
}
class Login extends Database {}
class PouleManipulation extends Database {}
$a = new Login();
$b = new PouleManipulation();
$b->fm = "New Value";
echo $a->fm; // prints "Value"
echo $b->fm; // prints "New Value"
What you're looking for is a static property, which is set at a class level. It's better if you access it through a static method. Here's a quick and dirty example, but you'd better use getters and setters.
class Database {
public static $fm = "Value";
}
class Login extends Database {}
class PouleManipulation extends Database {}
$a = new Login();
$b = new PouleManipulation();
$b::$fm = "New Value";
echo $a::$fm; // prints "New Value"
echo $b::$fm; // prints "New Value"
Edit:
I used the code you provided to explain how to achieve what you want, but I want to highlight what Ryan Vincent wrote in a comment: Login class should use (not extend) a Database class

Related

How to make an exception to "private" constructors?

I want to declare a non-public constructor so it's users of the class can't call new Message() directly but have to instantiate the object from a static builder method declared on an abstract class that Message extends.
So far my code is:
abstract class SqlDecodable {
public function instanceFromRawSql (array $rawSql) {
$newInstanceToReturn = new static() // problem is here
// .. map the new instance ..
return $newInstance ;
}
}
// for exemple...
class Message extends SqlDecodable {
private $receiverId ;
private $senderId ;
private $text ;
private/protected/public?? function __construct() {
// problem here : this constructor should be usable only by
parent class, not for user of Message
}
static function propertiesToSqlFields() {
return [
"receiverId" => "receiver_id_field_in_db",
"senderId" => "sender_id",
"text" => "text"
]
}
}
This is actually more complicated, but I simplified the system for this question
When I implement my method instanceFromRawSqlArray, I have to create a new instance of the child class: $instanceToReturn = new static(), and set the variables one by one afterwards.
Though, I don't want to let a __construct that takes no args in my model classes. I don't want the dev user of Message to be able to new Message().
This constructor should be usable only by instanceFromRawSqlArray.
The problem is that, as I saw, there is no C++ friends class in PHP. I can't make my __construct protected, because, as I saw, protected methods are accessibles for childs, not for parent.
Do you have ideas to map this new instance in the method instanceFromRawSqlArray, without creating any constructor or setter that would corrupt my model class "encapsulation protections"?
You were very close. You can simply declare your constructor to be protected.
Instantiating the class directly won't work, but you can call new from the static method declared in the abstract class.
E.g.:
abstract class SuperAbstract {
static function create() {
return new static();
}
}
class Extended extends SuperAbstract {
private $hello = '';
protected function __construct() {
$this->hello = "world";
}
public function hello() {
return "hello " . $this->hello;
}
}
// works
$a = Extended::create();
echo $a->hello(); // outputs "hello world"
// can't touch this. This will fail because the constructor is `protected`.
$b = new Extended();
Of course, since it's protected the constructor could also be called from children classes. That's unavoidable, as long as children classes are a possibility. But you could also declare Extended as final, making extension of the class impossible. Thus, it would only be possible to create new instances from the factory method defined in the abstract parent.
final Extended extends SuperAbstract
protected function __construct() { }
}
You can see it working (and failing), here: https://3v4l.org/LliKj

PHP Object Oriented array not being printed

Alright so I got 2 problems on my hands at the moment and those are
I want to call the function from the parent object but I am getting a lot of errors saying "Fatal error: Cannot instantiate abstract class Person"
If I call the getUserItems directly it will not do anything. There wont be anything echoed or such.
<?php
abstract class Person {
abstract function getUserItems();
}
class inventory extends Person {
protected $storage = array();
protected $item_id;
public function itemAdd($itemname) {
$storage[$this->item_id+1] = $itemname;
}
public function getUserItems() {
foreach($this->storage as $itemName=>$item_id) {
echo $itemName." ".$item_id."<br/>";
}
}
}
$user = new Person();
$user->getUserItems();
/*$user = new inventory();
$user->itemAdd("Item 1");
$user->itemAdd("Item 2");
$user->getUserItems();*/
?>
In OOP - abstract class may not be instantiated.
An abstract class is a class with both implementation and interface (pure virtual methods) that will be inherited. Interfaces generally do not have any implementation but only pure virtual functions.
So you cannot instantiate (new Person()). You must extend this abstract class and implement it's abtract functions (same way you did in inventory).
Regarding the echoing problem - inside your itemAdd function you didn't use the object's $storage member (you just used a local $storage variable). You should use it as the object's memebr, using $this->storage:
public function itemAdd($itemname) {
$this->storage[$this->item_id+1] = $itemname;
}
Note that I'm not so sure how you managed the $this->item_id member because you didn't set/changed it in the code.
If you only want to add new items to the storage member you could use:
$this->storage[] = $itemname;
This will make sure every new item will be added to the $storage array.
ad1)
Person is abstract class so you can not create object of that class.
ad2)
You must use $this
so not:
$storage[$this->item_id+1]
but
$this->storage[++$this->item_id]
Here I fixed another bug which was $this->item_id+1
Abstract class can not access directly but access from other class.
abstract class Person {
abstract function getUserItems();
}
class inventory extends Person {
protected $storage = array();
protected $item_id=2;
public function itemAdd($itemname) {
$storage[$this->item_id+1] = $itemname;
}
public function getUserItems() {
foreach($this->storage as $itemName=>$item_id) {
echo $itemName." ".$item_id."<br/>";
}
}
}
$user = new inventory();
$user->getUserItems();
$user = new inventory();
$user->itemAdd("Item 1");
$user->itemAdd("Item 2");
$user->getUserItems();

PHP use a class variable over several files?

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();
}

PHP - Static property in extended/child class

If I have a base abstract class like so:
<?php
abstract class Record {
static $table;
public function getRows () {
return getRowsFromTable(static::$table);
}
}
?>
And I want to extend this class like so:
<?php
class User extends Record {
static $table = 'users';
private $name;
?>
Then if I call:
<?php
$user = new User;
$user->getRows();
?>
Internally, getRows() calls and returns getRowsFromTable('users').
But if I were to create another class that also extends Record:
<?php
class House extends Record {
static $table = 'houses';
private $address;
?>
Then that static $table = 'houses'; declaration overrides the Record::$table and, consequently, breaks the User class.
What's happening is, the declaration static $table = 'houses'; bubbles up to the parent class, so now Record::$table = 'houses';. Since House was declared after User, the next time I call $user->getRows(), internally, User references parent Record and ultimately calls getRowsFromTable('houses') instead of getRowsFromTable('users').
I'm using late static binding so as to get the property from the extended class; but since User and House extend the same parent class, they both end up with the same property value although they override it with different values.
If I were to duplicate the Record class by creating a class Record2 and having House extend Record2, I wouldn't have this problem -- but that wouldn't really help.
Is this the wrong setup? Should I not be using static variables in this context? What should I put in their place, if so? I know that $table doesn't necessarily have to be static, but there are other properties that may need to be static.
I really wouldn't use a static string for this purpose.
How about something like this...
abstract class Record {
/**
* #return table name as string
*/
abstract protected function getTable();
public function getRows () {
return getRowsFromTable($this->getTable());
}
}
Then, your concrete implementations would have to implement getTable, eg
class User extends Record {
protected function getTable() {
return 'users';
}
}

Parent class property set by parent method blank when accessing from child method

I'm having trouble understanding why I can access a property from my parent class, but it's NULL, even though it has already been set by the parent (and has not been knowingly reset). I thought it might be because that property was set by a private method, but no difference when I changed to public. Here's a radically simplified example:
class TheParent
{
protected $_parent_property;
function __construct()
{}
private function parent_method($property);
{
$this->_parent_property = $property;
$call = new TheChild;
$call->child_method();
}
}
class TheChild extends TheParent
{
function __construct()
{
parent::construct();
}
public function child_method();
{
echo $this->_parent_property;
exit;
}
}
$test = new TheParent;
$test->parent_method('test');
I worked around this by passing the parent property to the child when the child is constructed by the parent ie new TheChild($this->_parent_property), but I still don't understand why $this->_parent_property is set to NULL when accessed from the child in my original example.
I do know that if I set this property from the parent constructor, I'd be able to access it just fine. I'm trying to understand why a property set by a parent method, and accessible by other parent methods, is not accessible from the child class which extends the parent.
Can anyone explain? Thanks!
The problem is that you're creating a new instance where the variable isn't set. The property is bound to a particular instance, so you're creating one instance of the parent and then from the parent another instance of the child,i which includes all the stuff creating a new parent would contain, including $_parent_property. When you read the value in the child, you're reading the value of a newly created parent, not the one you previously created.
In effect, you do this:
A = new TheParent()
A->_parent_property = 'test'
Calls:
B = new TheChild() underneath the covers, this does new TheParent()
Print B->_parent_property (which was uninitialized)
Consider this similar example that will produce your expected result:
class TheParent
{
protected $_parent_property;
function __construct()
{
parent_method();
}
private function parent_method();
{
$this->_parent_property = 'test';
}
}
class TheChild extends TheParent
{
function __construct()
{
parent::construct();
}
public function child_method();
{
echo $this->_parent_property;
exit;
}
}
$child = new TheChild();
$child->child_method();
In this example, the private method in TheParent is invoked on the same instance created by TheChild, setting the underlying instance variable.
You have a slightly wrong idea of how inheritance works.
TheParent is a class, and TheChild is a class based on the TheParent. $test now is an instance of TheParent. It has no idea that there is another class TheChild based on the class TheParent.
You create a new instance $call which is of type TheChild. This is, to use another word, a new object. It has nothing to do with $test, except that both are "compatible" to TheParent.
TheChild ($call) inherits the property _parent_property from its parent (class). However, that property is not initialised/set in that instance (object), so it is still NULL.

Categories