PHP Object Oriented array not being printed - php

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

Related

Calling an external class only once?

I have a class that using an external package to do something
class MyClass
{
public function doSomething($data){
$external = new External();
$external->doSomething($data);
}
}
This class is called from another class, for example:
class MasterClass
{
public function go(){
$data = 'whatever';
$data2 = 'whatever2';
$myClass = new MyClass();
$myClass->doSomething($data);
$myClass->doSomething($data2);
....
}
}
So in my MasterClass I am calling the doSomething function multiple times. WHich creates a new External class multiple times - which is not really necessary.
How can I get around this issue and only create the external class once?
class MyClass
{
protected $external;
public function doSomething($data){
if(!$this->external){
$this->external = new External();
}
$this->external->doSomething($data);
}
}
But read about dependency injection in php.
Move the new External() call into the constructor and store it as a property, then reference that property in the doSomething() method instead of constructing a new instance every time.
Alternatively, if you don't want to always construct a new External whenever you construct a MyClass, you could move the construction into a Lazy Load static method called something like getExternal() in External class.
The first time that method is called it would need to store a new instance of External as a property, but on subsequent calls (when the property is already set) return the same instance. This type of pattern is called Singleton Pattern in Object-Oriented Design Patterns.
You could refer to this link to know more about singleton pattern and how it is implemented.
Pass External class in constructor.
class MyClass
{
private $external;
public function __construct(External $external)
{
$this->external = $external;
}
public function doSomething($data)
{
$this->external->doSomething($data);
}
class MasterClass
{
public function go() {
$data = 'whatever';
$data2 = 'whatever2';
$external = new External();
$myClass = new MyClass($external);
$myClass->doSomething($data);
$myClass->doSomething($data2);
....
}
}

Factory Method: Prevent a class from Direct Instancing

I have a Factory Method to instance a class. Is there a way to prevent this class from direct instancing?
The only option I see is to use an argument passed into the __construct(), but that's not something I'm looking for.
On the other hand, making the __construct() private would be ideal, but I don't want MyClass to extend the Factory without actual need.
What do you guys think?
Factory Method:
class Factory
{
public static function instance()
{
return new MyClass(true);
}
}
MyClass:
class MyClass
{
public function __construct($isFactory = false)
{
if (!$isFactory) {
throw new Exception('Use Factory::instance() to create an object');
}
}
}
There are hacks to do that:
abusing inheritance to use a protected constructor
putting the factory method inside the class so that it can call the private constructor, which is actually not a hack. But then why not using the constructor in the first place?
using reflection to access the private constructor
I'm not promoting anything of that. What I personally do is documenting the API with things like #internal and leave it to the client following that contract.
In essence, your code should have read something like this:
THE FACTORY
<?php
class Factory {
public static function instance(){
return new MyClass(true); //HERE YOU ARE INSTANTIATING
}
}
THE CLASS TO BE INSTANTIATED VIA THE FACTORY
<?php
//NOT MyClass() <--- YOU ARE DEFINING.... NOT INSTANTIATING...
class MyClass {
public function __construct($isFactory = false) {
if (!$isFactory) {
throw new Exception('Use Factory::instance() to create an object');
}
}
//...MORE METHODS
}
Could you try this instead?
<?php
class Factory
{
private static $FACTORY_GUARANTOR; //ONLY SET DURING INSTANTIATION
public static function instance($type) {
if (class_exists($type)) {
self::$FACTORY_GUARANTOR = 1;
$instance = new $type();
self::$FACTORY_GUARANTOR = null;
return $instance;
}
else {
throw new Exception("Class not found...");
}
}
//YOU CAN GET $FACTORYGUARANTOR EXTERNALLY BUT NEVER SET IT;
public static function getGuarantor(){
return self::$FACTORY_GUARANTOR;
}
}
class MyClass {
protected $property1;
protected $property3;
protected $property2;
public function __construct() {
// IF SOMEONE TRIES TO INSTANTIATE THE CLASS OUTSIDE OF THE FACTORY... BLOW A WHISTLE
if(!Factory::getGuarantor()){
throw new Exception('Use Factory::instance() to create an object');
}
// IF THE PROGRAM MADE IT TO THIS POINT;
// JUST INSTANTIATE THE CLASS BECAUSE MOST LIKELY IT IS COMING FROM THE FACTORY
var_dump($this); // A LITTLE CONFIRMATION....
}
//...MORE METHODS
}
// TRY IT OUT:
/*INSTANCE A: RIGHT*/ $theClass = Factory::instance("MyClass"); //INSTANTIATES THE CLASS
/*INSTANCE B: WRONG*/ $theClass = new MyClass(); //THROWS AN EXCEPTION
The easiest way is to define your base class as abstract. The abstract classes cannot be directly instanced, so you will have to redefine their abstract members in the inherited classes:
abstract class Factory
{
abstract public function foo();
}
class InheritedClass extends Factory
{
public function foo()
{
// Do something
}
}
// $obj1 = new Factory(); // Will produce an error
$obj1 = new InheritedClass(); // Will be executed successfully
You can read more for the abstract classes here: PHP: Class Abstraction - Manual.
For me, the best way is to use ReflectionClass:
class MyClass
{
public const FRIEND_CLASSES = [Factory::class];
protected function __construct() {}
}
trait Constructor
{
protected function createObject(string $className, array $args = [])
{
if (!in_array(static::class, $className::FRIEND_CLASSES)) {
throw new \Exception("Call to private or protected {$className}::__construct() from invalid context");
}
$reflection = new ReflectionClass($className);
$constructor = $reflection->getConstructor();
$constructor->setAccessible(true);
$object = $reflection->newInstanceWithoutConstructor();
$constructor->invokeArgs($object, $args);
return $object;
}
}
class Factory
{
use Constructor;
public function MyClass(): MyClass
{
return $this->createObject(MyClass::class);
}
}
In constant FRIEND_CLASSES you can define in which classes the class can be instanced.
trait is used because this functionality can be used in different factories that are not related.
If you need to put parameters into constructor of the class, put them as second parameter of createObject.
Details I described in the article "Forbidding of creating objects outside factory in PHP"

PHP Keep parent class properties when using a child class constructor

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

PHP Object being passed to class, not being accepted

I have a database class that has a series of functions in it, and I have a Main class that has the dependencies injected into it with other classes such as Users, Posts, Pages etc extending off of it.
This is the main class that has the database dependency injected into it.
class Main {
protected $database;
public function __construct(Database $db)
{
$this->database = $db;
}
}
$database = new Database($database_host, $database_user, $database_password, $database_name);
$init = new Main($database);
And then, this is the Users class I'm extending off of it.
class Users extends Main {
public function login() {
System::redirect('login.php');
}
public function view($username) {
$user = $this->database->findFirst('Users', 'username', $username);
if($user) {
print_r($user);
} else {
echo "User not found!";
}
}
}
But, whenever trying to call the view function for the User class, I'm getting this error Catchable fatal error: Argument 1 passed to Main::__construct() must be an instance of Database, none given. And, if I remove the Database keyword from the _construct parameters, I get this error instead Warning: Missing argument 1 for Main::_construct().
If I pass a variable to the User class from main class it works, but not if I'm trying to pass the Database object, I just can't work out why.
The User class is instantiated via a router with no parameters passed to it.
You can make the $database variable in the Main class static. Then you need to initialize it only once:
class Main {
static $database = null;
public function __construct($db = null)
{
if (self::$database === null && $db instanceof Database) {
self::$database = $db;
}
}
}
The class User extends Main and therefore it inherits the __construct function of the Main class.
You can't instantiate the User class without passing its dependency, the Database instance.
$database = new Database($database_host, $database_user, $database_password, $database_name);
$user = new User($database);
However, you can do it like matewka hinted:
class Main {
static $database = null;
public function __construct($db = null)
{
if (self::$database === null && $db instanceof Database) {
self::$database = $db;
}
}
}
class User extends Main{
function test()
{
return parent::$database->test();
}
}
class Database{
function test()
{
return "DATABASE_TEST";
}
}
$db = new Database();
$main = new Main($db);
$user = new User();
var_dump($user->test());
As clearly noted in the other answers/comments you are not passing the expected dependency into your Users class to the __construct() method it inherits from Main.
In other words, Users expects to be instantiated in the same manner as Main; i.e:
$database = new Database();
$users = new Users($database);
However, I would like to add some detail because constructors in PHP are a little different to other methods when you are defining inheritance relationships between classes, and I think it's worth knowing.
Using inheritance, you can of course override a parent's method in an inheriting class, with a specific caveat in PHP: if you don't match the argument signature of the overridden method you trigger a E_STRICT warning. This is not a showstopper but it's best avoided when endeavouring to write stable robust code:
E_STRICT Enable to have PHP suggest changes to your code which will ensure the best interoperability and forward compatibility of your code. Source
An example:
class Dependency
{
// implement
}
class Foo
{
function doSomething(Dependency $dep)
{
// parent implementation
}
}
class Bar extends Foo
{
function doSomething()
{
// overridden implementation
}
}
$bar = new Bar();
$bar->doSomething();
If you run this code or lint it on the command line with php -l you'll get something like:
PHP Strict standards: Declaration of Bar::doSomething() should be compatible with Foo::doSomething(Dependency $dep) in cons.php on line 22
The reason why I am pointing this out is because this does not apply to __construct:
Unlike with other methods, PHP will not generate an E_STRICT level error message when __construct() is overridden with different parameters than the parent __construct() method has. Source (#Example 1)
So you can safely override __construct in an inheriting class and define a different argument signature.
This example is completely valid:
class Foo
{
function __construct(Dependency $dep)
{
// parent constructor
}
}
class Bar extends Foo
{
function __construct()
{
// overridden constructor
}
}
$bar = new Bar();
Of course, this is dangerous because in the highly likely event inheriting class Bar calls code that relies on Dependency you're going to get an error because you never instantiated or passed the dependency.
So you really should ensure the Dependency in the case above is passed to the parent class. So you are right back where you started, passing the dependency in the constructor, or doing the following, using the parent keyword to call the parent's __construct method:
class Bar extends Main
{
public function __construct()
{
// call the Main constructor and ensure
// its expected dependencies are satisfied
parent::__construct(new Dependency());
}
}
However, best practice dictates that you should pass dependencies into your object instead of instantiating them in your classes, a.k.a dependency injection. So we arrive back at the original solution provided by the other answers :)
$database = new Database();
$users = new Users($database);
Allowing overriding of __construct with different method signatures simply allows greater flexibility when defining classes - you might have am inheriting class that requires more information to be instantiated. For a (ridiculously contrived) example:
class Person
{
public function __construct($name)
{
// implement
}
}
class Prisoner extends Person
{
public function __construct($name, $number)
{
// implement
}
}
$p1 = new Person('Darragh');
$p2 = new Prisoner('Darragh', 'I AM NOT A NUMBER!');
I am pointing this out because __construct is actually quite flexible, if you find yourself in a situation where you want to instantiate an inheriting class with different arguments to the parent, you can, but with the big caveat that you must somehow ensure that you satisfy all expected dependencies of the parent class.
Hope this helps :)

Calling an Abstract method from parent abstract class

After looking at this question, I tried the chosen answer myself!
So basically what I wrote was `
abstract class person
{
function __construct()
{
// some code here
}
function myfunc()
{
$this->test();
}
abstract function test();
}
class employee extends person
{
function __construct()
{
parent::__construct();
}
function test()
{
echo "So you managed to call me !!";
}
}
$employee = new employee();
$employee->myfunc();`
When I run this script, I got an usual error
Fatal error: Using $this when not in object context on line 7.
So, how do I call an Abstract from the Parent Class?
EDIT
The real thing which I am trying to do is create a DatabaseObject class which holds all the common methods of different database objects and make classes extend it.
abstract class DatabaseObject {
abstract protected function getTableName();
public static function find_by_id($id = 0){
global $database;
$class = get_called_class();
$result_array = self::find_by_sql("SELECT * FROM ". $this->getTableName() ." WHERE _id = {$id} LIMIT 1");
echo $result_array . "<br />";
return !empty($result_array) ? array_shift($result_array) : false;
}
public static function find_by_sql($sql=""){
global $database;
$result_set = $database->query($sql);
$object_array = array();
while ($row = $database->fetch_array($result_set)){
$object_array[] = self::instantiate($row);
}
return $object_array;
}
}
}
So this is some part of my DatabaseObject Class. My User class is as follows
class User extends DatabaseObject {
protected static $table_name = "users";
protected function getTableName(){
global $table_name;
return $table_name;
}
}
Now, whenever I do
$user = new User();
$user->findById(1);
I get the error Fatal error: Using $this when not in object context
on the line which points to
$this->getTableName();
I hope this might help in clearing up some stuff. Is there any way I can achieve this?
Thanks!
Your code example works just fine, I don't understand your issue Demo.
So the code you've shared is not the code you talk about with the error .
You can not call the variable as if it were a member of the class, since you declared it static. Calls to static variables looks the same as when you call static functions, use the :: operator.
class User extends DatabaseObject {
protected static $table_name = "users";
protected function getTableName(){
return self::$table_name;
}
}
The first thing is you can not call the abstract function within the same class. You are calling test(); in side myfunc() which is not allowed by oops.
You cant access the abstract function inside the same abstract class.
What ever your needs are, you have to call that method in sub class only, abstract super class will not call the sub class function.
EDIT:
Because the function dosent exists in that class.

Categories