How to call different class members in php without conflict? - php

I have 4 classes, 1 of them is the main class that holds all the general objects.
3 others are situation classes (for the lack of a better word, as they are included in different parts of the script as needed)
I would like to access all three classes' methods and objects from the main class (or at least just the methods) AND access the main class' methods and variables from the other three classes, AND share the access between all the classes, so that I can call any method of any class from any class.
I know I can make the other three classes members of the main class like this:
class Main
{
public $test = 'test';
public function __construct ($classA, $classB, $classC)
{
$this -> classA = $classA;
$this -> classB = $classB;
$this -> classC = $classC;
echo 'Main just got constructed!';
}
public function test()
{
echo $this -> test;
}
}
class A extends Main
{
public function TestA()
{
echo 'In Class A';
}
}
class B extends Main
{
public function __construct()
{
echo 'B just got constructed!';
}
public function TestB()
{
echo 'In Class B';
}
}
class C extends Main
{
public function TestC()
{
echo 'In Class C';
// can we access B from here? probably not.
// would B::TestB() work?
B::TestB();
// yes it does.
// but is there any other way of accessing the method? like $this -> TestB()? apart from extending the B class to C.
// Is there any downside to using this way of accessing methods and variables? what are the cons?
}
}
$A = new A;
$B = new B;
$C = new C;
$Main = new Main ($A, $B, $C);
$C -> TestC();
$Main -> classC -> TestC();
The first issue with this is, extending the Main class to other classes throws warnings because of the missing arguments for __construct() in the Main class:
Warning: Missing argument 1 for Main::__construct()
The other concern is that if I use this method, then all three classes have to be initiated before the Main Class. But My script requires the main class to be executed at the very start as it establishes connections to mysql and sets environment variables etc. Plus it is not necessary that all three classes will be used at the same time, so in some part of the websites, only one class is required and it is pointless to initiate the other classes in those parts. (I have more than 4 classes, this is just to make things simpler). In that case, how do I make the other classes members of Main after it has been initiated?
Another thing, extending Main class thrice in other classes seems to initiate Main class thrice in separately.. which is resetting the main class' objects and they are not the same when accessed with other classes. I found out about that issue when the test code threw "Main just got constructed!" thrice and it was setting the variables to their default values. How to avoid that? I would like the Main class' variables to have same values across all the other classes.. kind of like global variables.

Well, as you have objects "injected" later or before Main instance is created, maybe you want add um method register in Main class. Also, as long as each class must have access to onother (such as linking) you could add an attribute (maybe $main) with instance of Main class.
I think it could be useful:
<?php
class A
{
/**
* #var Main
*/
public $main; # Main class
public function test()
{
#$this->main->aInstance->someMethod();
#$this->main->bInstance->someMethod();
// ...
return __METHOD__;
}
}
class B
{
/**
* #var Main
*/
public $main;
public function test()
{
return __METHOD__;
}
}
class Main
{
public $objectPool = array();
public function __construct(array $objects = array())
{
foreach ($objects as $object) {
$object->main = $this;
# add...
$this->addObject($object);
}
}
public function addObject($object)
{
$this->objectPool[ get_class($object) ] = $object;
}
public function getObject($classname)
{
if (array_key_exists($classname, $this->objectPool)) {
return $this->objectPool[$classname];
}
return null; # Or raise an Excetion?
}
public function test()
{
return get_class($this);
}
}
$obj = array(new A, new B);
$main = new Main($obj);
var_dump($main->getObject('B')->main->test());
var_dump($main->getObject('B')->test());
var_dump($obj[1]->main->getObject('A')->test());
However, I think these objects know much about each (which is not a good idea - bad design, actually).
Here goes the runnable code: http://3v4l.org/Y19Kr

Related

PHP Use alternate class if present

I'm trying to covert old code that uses functions to use classes. Some of the old code has options only available if an advanced option is set. In the base class I have put all of the functions (methods). If the function is available as an advanced option it is overriden in the second class. If not, it should say not available in the base class. The problem is that I can't figure out how to call one class or the other, short of putting in a bunch of if's, of course.
The original function would look like this
function Testclass() {
if (advanced_enabled)
return 'Do advacned stuff<br>';
else
return 'Do base stuff<br>';
}
Here are my classes:
class A {
public function Testclass() {
return 'in base class<br>';
}
public function SomeBaseCode() {
}
}
class B {
public function Testclass() {
return 'in advanced class<br>';
}
}
If I do this:
$a = new A();
echo 'base '.$a->Testclass();
$b = new B();
echo 'base '.$b->Testclass();
The output is
in base class
in advanced class
What I'm wanting to do is have the advanced class used if present. But the base class has to be present because it has methods always available. I can do this
$a = new A();
echo 'base '.$a->Testclass();
if (advanced_enabled) {
$b = new B();
echo 'base '.$b->Testclass();
}
But that gives me two different class variables and I would have to edit a lot of code to check each. I'm fairly new to classes so maybe I am missing some basic idea. Is there a way to do this?
Your advanced class B needs to extend from the base class A.
class A {
public function Testclass() {
return 'in base class<br>';
}
public function SomeBaseCode() {
}
}
class B extends A {
public function Testclass() {
return 'in advanced class<br>';
}
}
Instantiation is based on the advanced_enabled flag.
$a = advanced_enabled ? new A() : new B();
$a->SomeBaseCode();
$a->Testclass();
Instead of directly creating the class using new, use a Factory to do this for you. This factory can be a different class, or a simple function. If you use any PHP framework, you're probably using dependency injection, which can take care of this for you.
If you do not want to re-define all methods (i.e. some methods in B should be exactly the same as in A), you can use the extend keyword to have class B inherit all methods from class A you did not override. This principle is called object inheritance.
In pseudo-PHP, it would look a bit like this:
// Define classes (interface is optional, but recommended)
interface someInterface {
public function testClass();
public function someOtherFunction();
}
class simpleVersion implements someInterface {
public function testClass() { /* ... */ }
public function someOtherFunction() { /* ... */ }
}
class advancedVersion extends simpleVersion {
public function testClass() { /* ... */ }
// someOtherFunction is not defined here. but is still usable!
}
// Create the factory responsible for instantiating the class
function createVersion() {
return $advancedEnabled ? new advancedVersion(); : new simpleVersion();
}
// Create the class instance (dynamically) and use it
$class = createVersion(); // $class is now either simpleVersion or advancedVersion.
$class->testClass();
Also, if you want a class B method to do something 'advanced', while also keeping the functionality of class A, you can use the special parent::something(); call to 'copy' the functionality of the base class:
class Shouter() {
public function shout($text) {
return strtoupper($text);
}
}
class LoudShouter() extends Shouter {
public function shout($text) {
return parent::shout($text) . '!!!';
}
}
$text = 'Hello World';
$shouter1 = new Shouter();
$shouter2 = new LoudShouter();
var_dump(
$text,
$shouter1->shout($text),
$shouter2->shout($text)
);
// Output:
// -> Hello World
// -> HELLO WORLD
// -> HELLO WORLD!!!

PHP - Call a method from the class where i instantiated my object

I have 2 classes declared like in the example below.
class A{
protected $process;
public function __construct() {
......
$this->process=new B();
}
public function do_something(){
....
}
}
class B{
// content not important
// I need to call do_something from class A
}
My question is, how can I call from class B the method do_something() from class A? Is it possible?
From your example it is impossible for instance of B to know that it is instantiated and stored by an instance of class A. You need to create that connection explicitly in some way.
I didn't think this would even work, but apparently you can pass instance of A to B before A is even done with its constructor:
class A {
protected $process;
public function __construct() {
$this->process = new B( $this );
}
public function do_something() {
var_dump( 'do_something' );
}
public function test() {
$this->process->test();
}
}
class B {
public function __construct( A $a ) {
$this->a = $a;
}
public function test() {
$this->a->do_something();
}
}
$a = new A();
$a->test(); // do_something
It's hard to give an advice on what the best approach for your particular case would be, as we don't know what either A or B does.
There's a few ways to achieve this. One way would be to make B and extension of A - thereby allowing all methods of the class A to be callable on the object B. Another way is to create a new object of A inside B and call that method. Or you can pass
Here's an example where B is extended from A. By doing this, all properties and methods of A can be called on B, unless overwritten in B.
class A {
public function doSomething(){
echo "doSomething() called in A";
}
}
class B extends A {
public function someMethod() {
$this->doSomething();
}
}
$b = new B();
$b->someMethod();
The above would output doSomething() called in A.
Or, you can create an object A and call that method inside B.
class B {
public function someMethod() {
$a = new A();
$a->do_something();
}
}
$b = new B();
$b->someMethod();
After reading all the answers and doing some research i think that the best method for me was the use of Traits
"Traits are a mechanism for code reuse in single inheritance languages such as PHP. A Trait is intended to reduce some limitations of single inheritance by enabling a developer to reuse sets of methods freely in several independent classes living in different class hierarchies."
So i declared a Trait with the method do_something and call that method from Both class A and Class B
Thanks

PHP Sibling Class Inheritance

I'm a bit confused on whether or not this is possible. I've checked a couple of posts here on SO and they don't really explain what I'm looking for.
I have 3 classes. One main class and two classes extending that main class. (see code below). Is it possible to run a method in one of the two extended classes from it's sibling (the other extended class)?
If it's not possible, how can I change my code to accomplish what I'm doing in the example below?
DECLARATION
class A {
public function __construct() {
//do stuff
}
}
class B extends A {
private $classb = array();
public function __construct() {
parent::__construct();
//do stuff
}
public function get($i) {
return $this->classb[$i];
}
public function set($i, $v) {
$this->classb[$i] = $v;
}
}
class C extends A {
public function __construct() {
parent::__construct();
//do stuff
}
public function display_stuff($i) {
echo $this->get($i); //doesn't work
echo parent::get($i); //doesn't work
}
}
USAGE
$b = new B();
$c = new C();
$b->set('stuff', 'somestufftodisplay');
$c->display_stuff('stuff'); // <----- Displays nothing.
Your code shows an additional problem apart from the main question so there are really two answers:
No, you cannot run a method from a sibling class in another sibling class. If you need that, the method should be in the parent class. The same applies to properties.
You cannot use the value of a property from one object in another object, even if they are both of the same class. Setting a property value in one object sets its value only there as different objects can have the same properties with completely different values. If you need to share the value of a property between the objects and also be able to modify it, you should use a static property. In this case you would have to define that in the parent class, see my previous point.
So to make it work, you would need something like
class A {
private static $var = array();
public function get($i) {
return self::$var[$i];
}
public function set($i, $v) {
self::$var[$i] = $v;
}
}
class B extends A {
}
class C extends A {
public function display_stuff($i) {
echo $this->get($i); // works!
}
}
$b = new B();
$c = new C();
$b->set('stuff', 'somestufftodisplay');
$c->display_stuff('stuff');
An example.

Nested or Inner Class in PHP

I'm building a User Class for my new website, however this time I was thinking to build it little bit differently...
C++, Java and even Ruby (and probably other programming languages) are allowing the use of nested/inner classes inside the main class, which allows us to make the code more object-oriented and organized.
In PHP, I would like to do something like so:
<?php
public class User {
public $userid;
public $username;
private $password;
public class UserProfile {
// some code here
}
private class UserHistory {
// some code here
}
}
?>
Is that possible in PHP? How can I achieve it?
UPDATE
If it's impossible, will future PHP versions might support nested classes?
Intro:
Nested classes relate to other classes a little differently than outer classes. Taking Java as an example:
Non-static nested classes have access to other members of the enclosing class, even if they are declared private. Also, non-static nested classes require an instance of the parent class to be instantiated.
OuterClass outerObj = new OuterClass(arguments);
outerObj.InnerClass innerObj = outerObj.new InnerClass(arguments);
There are several compelling reasons for using them:
It is a way of logically grouping classes that are only used in one place.
If a class is useful to only one other class, then it is logical to
relate and embed it in that class and keep the two together.
It increases encapsulation.
Consider two top-level classes, A and B, where B needs access to
members of A that would otherwise be declared private. By hiding class
B within class A, A's members can be declared private and B can access
them. In addition, B itself can be hidden from the outside world.
Nested classes can lead to more readable and maintainable code.
A nested class usually relates to it's parent class and together form a "package"
In PHP
You can have similar behavior in PHP without nested classes.
If all you want to achieve is structure/organization, as Package.OuterClass.InnerClass, PHP namespaces might sufice. You can even declare more than one namespace in the same file (although, due to standard autoloading features, that might not be advisable).
namespace;
class OuterClass {}
namespace OuterClass;
class InnerClass {}
If you desire to emulate other characteristics, such as member visibility, it takes a little more effort.
Defining the "package" class
namespace {
class Package {
/* protect constructor so that objects can't be instantiated from outside
* Since all classes inherit from Package class, they can instantiate eachother
* simulating protected InnerClasses
*/
protected function __construct() {}
/* This magic method is called everytime an inaccessible method is called
* (either by visibility contrains or it doesn't exist)
* Here we are simulating shared protected methods across "package" classes
* This method is inherited by all child classes of Package
*/
public function __call($method, $args) {
//class name
$class = get_class($this);
/* we check if a method exists, if not we throw an exception
* similar to the default error
*/
if (method_exists($this, $method)) {
/* The method exists so now we want to know if the
* caller is a child of our Package class. If not we throw an exception
* Note: This is a kind of a dirty way of finding out who's
* calling the method by using debug_backtrace and reflection
*/
$trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3);
if (isset($trace[2])) {
$ref = new ReflectionClass($trace[2]['class']);
if ($ref->isSubclassOf(__CLASS__)) {
return $this->$method($args);
}
}
throw new \Exception("Call to private method $class::$method()");
} else {
throw new \Exception("Call to undefined method $class::$method()");
}
}
}
}
Use case
namespace Package {
class MyParent extends \Package {
public $publicChild;
protected $protectedChild;
public function __construct() {
//instantiate public child inside parent
$this->publicChild = new \Package\MyParent\PublicChild();
//instantiate protected child inside parent
$this->protectedChild = new \Package\MyParent\ProtectedChild();
}
public function test() {
echo "Call from parent -> ";
$this->publicChild->protectedMethod();
$this->protectedChild->protectedMethod();
echo "<br>Siblings<br>";
$this->publicChild->callSibling($this->protectedChild);
}
}
}
namespace Package\MyParent
{
class PublicChild extends \Package {
//Makes the constructor public, hence callable from outside
public function __construct() {}
protected function protectedMethod() {
echo "I'm ".get_class($this)." protected method<br>";
}
protected function callSibling($sibling) {
echo "Call from " . get_class($this) . " -> ";
$sibling->protectedMethod();
}
}
class ProtectedChild extends \Package {
protected function protectedMethod() {
echo "I'm ".get_class($this)." protected method<br>";
}
protected function callSibling($sibling) {
echo "Call from " . get_class($this) . " -> ";
$sibling->protectedMethod();
}
}
}
Testing
$parent = new Package\MyParent();
$parent->test();
$pubChild = new Package\MyParent\PublicChild();//create new public child (possible)
$protChild = new Package\MyParent\ProtectedChild(); //create new protected child (ERROR)
Output:
Call from parent -> I'm Package protected method
I'm Package protected method
Siblings
Call from Package -> I'm Package protected method
Fatal error: Call to protected Package::__construct() from invalid context
NOTE:
I really don't think trying to emulate innerClasses in PHP is such a good idea. I think the code is less clean and readable. Also, there are probably other ways to achieve similar results using a well established pattern such as the Observer, Decorator ou COmposition Pattern. Sometimes, even simple inheritance is sufficient.
Real nested classes with public/protected/private accessibility were proposed in 2013 for PHP 5.6 as an RFC but did not make it (No voting yet, no update since 2013 - as of 2021/02/03):
https://wiki.php.net/rfc/nested_classes
class foo {
public class bar {
}
}
At least, anonymous classes made it into PHP 7
https://wiki.php.net/rfc/anonymous_classes
From this RFC page:
Future Scope
The changes made by this patch mean named nested classes are easier to implement (by a tiny bit).
So, we might get nested classes in some future version, but it's not decided yet.
You cannot do this in PHP. However, there are functional ways to accomplish this.
For more details please check this post:
How to do a PHP nested class or nested methods?
This way of implementation is called fluent interface: http://en.wikipedia.org/wiki/Fluent_interface
As per Xenon's comment to Anıl Özselgin's answer, anonymous classes have been implemented in PHP 7.0, which is as close to nested classes as you'll get right now. Here are the relevant RFCs:
Nested Classes (status: withdrawn)
Anonymous Classes (status: implemented in PHP 7.0)
An example to the original post, this is what your code would look like:
<?php
public class User {
public $userid;
public $username;
private $password;
public $profile;
public $history;
public function __construct() {
$this->profile = new class {
// Some code here for user profile
}
$this->history = new class {
// Some code here for user history
}
}
}
?>
This, though, comes with a very nasty caveat. If you use an IDE such as PHPStorm or NetBeans, and then add a method like this to the User class:
public function foo() {
$this->profile->...
}
...bye bye auto-completion. This is the case even if you code to interfaces (the I in SOLID), using a pattern like this:
<?php
public class User {
public $profile;
public function __construct() {
$this->profile = new class implements UserProfileInterface {
// Some code here for user profile
}
}
}
?>
Unless your only calls to $this->profile are from the __construct() method (or whatever method $this->profile is defined in) then you won't get any sort of type hinting. Your property is essentially "hidden" to your IDE, making life very hard if you rely on your IDE for auto-completion, code smell sniffing, and refactoring.
Since PHP version 5.4 you can force create objects with private constructor through reflection. It can be used to simulate Java nested classes. Example code:
class OuterClass {
private $name;
public function __construct($name) {
$this->name = $name;
}
public function getName() {
return $this->name;
}
public function forkInnerObject($name) {
$class = new ReflectionClass('InnerClass');
$constructor = $class->getConstructor();
$constructor->setAccessible(true);
$innerObject = $class->newInstanceWithoutConstructor(); // This method appeared in PHP 5.4
$constructor->invoke($innerObject, $this, $name);
return $innerObject;
}
}
class InnerClass {
private $parentObject;
private $name;
private function __construct(OuterClass $parentObject, $name) {
$this->parentObject = $parentObject;
$this->name = $name;
}
public function getName() {
return $this->name;
}
public function getParent() {
return $this->parentObject;
}
}
$outerObject = new OuterClass('This is an outer object');
//$innerObject = new InnerClass($outerObject, 'You cannot do it');
$innerObject = $outerObject->forkInnerObject('This is an inner object');
echo $innerObject->getName() . "\n";
echo $innerObject->getParent()->getName() . "\n";
You can't do it in PHP. PHP supports "include", but you can't even do that inside of a class definition. Not a lot of great options here.
This doesn't answer your question directly, but you may be interested in "Namespaces", a terribly ugly\syntax\hacked\on\top\of PHP OOP:
http://www.php.net/manual/en/language.namespaces.rationale.php
I think I wrote an elegant solution to this problem by using namespaces. In my case, the inner class does not need to know his parent class (like the static inner class in Java). As an example I made a class called 'User' and a subclass called 'Type', used as a reference for the user types (ADMIN, OTHERS) in my example. Regards.
User.php (User class file)
<?php
namespace
{
class User
{
private $type;
public function getType(){ return $this->type;}
public function setType($type){ $this->type = $type;}
}
}
namespace User
{
class Type
{
const ADMIN = 0;
const OTHERS = 1;
}
}
?>
Using.php (An example of how to call the 'subclass')
<?php
require_once("User.php");
//calling a subclass reference:
echo "Value of user type Admin: ".User\Type::ADMIN;
?>
You can, like this, in PHP 7:
class User{
public $id;
public $name;
public $password;
public $Profile;
public $History; /* (optional declaration, if it isn't public) */
public function __construct($id,$name,$password){
$this->id=$id;
$this->name=$name;
$this->name=$name;
$this->Profile=(object)[
'get'=>function(){
return 'Name: '.$this->name.''.(($this->History->get)());
}
];
$this->History=(object)[
'get'=>function(){
return ' History: '.(($this->History->track)());
}
,'track'=>function(){
return (lcg_value()>0.5?'good':'bad');
}
];
}
}
echo ((new User(0,'Lior','nyh'))->Profile->get)();
It is waiting for voting as RFC
https://wiki.php.net/rfc/anonymous_classes
This page keeps coming up in my Internet searches on this subject so figured I should chime in even though this is an 8-year old post. The documentation for PHP5 demonstrates that anonymous classes can be defined within a class method. The object created can extend, implement, and even use other classes, interfaces, and traits. Consider the following OOP paradigm of factory object production. Similar to what #e-i-pi pointed out ...
class Factory {
/**
* Method to manufacture an inner-class object.
*
* #param string $args Arguments to be passed to
* the inner-class constructor.
*/
static function manufacture_object($args) {
/**
* Here's the definition of the inner-class.
*/
return new class($args) {
static $remembers = 'Nothing';
private $args;
function __construct($args) {
$this->$args = $args;
}
function says() {
return $this->args;
}
};
}
}
/**
* Create an inner-class object and have it do its thing.
*/
$mort = Factory::manufacture_object("Hello World!");
echo $mort->says(); // Echoes "Hello World!"
The objects are one-off, so one would expect the static values of the objects returned would not bind from one instance to another. After all, the anonymous class is unique from one object to another. However, late static binding works as one would otherwise expect from a nested class.
$mort = Factory::manufacture_object("I can remember that.");
$mort2 = Factory::manufacture_object("I'll live vicariously through you.");
$mort::$remembers = 'Something';
echo $mort2::$remembers; // Echoes "Something"
So, there you go: inner/nested classes and creation of their objects with static functionality has been possible since September 22, 2013 (right about the time this question was asked).
Put each class into separate files and "require" them.
User.php
<?php
class User {
public $userid;
public $username;
private $password;
public $profile;
public $history;
public function __construct() {
require_once('UserProfile.php');
require_once('UserHistory.php');
$this->profile = new UserProfile();
$this->history = new UserHistory();
}
}
?>
UserProfile.php
<?php
class UserProfile
{
// Some code here
}
?>
UserHistory.php
<?php
class UserHistory
{
// Some code here
}
?>

php class extends, variable from other extended class

I have a main class and two extended classes:
class Main {
public $foo;
}
class A extends Main {
public function setVar()
{
$foo = "test";
}
}
class B extends Main {
public function getVar()
{
return $this->foo;
}
}
$A = new A;
$B = new B;
$A->setVar();
echo "result: ".$B->getVar();
But result ($B->getVar()) stays empty. I am clearly missing something simple here... Besides that, is this the way to go splitting long classes up in relevant subclasses?
change:
class A extends Main {
public function setVar()
{
$foo = "test";
}
}
to:
class A extends Main {
public function setVar()
{
$this->foo = "test";
}
}
Notice the $this keyword
Example of using Dependency Injection (DI) to share an instance of Main between to different class instances rather than trying to use inheritence
class Main {
public $foo;
}
class A {
protected $main;
public function setVar($data)
{
$this->main->foo = $data;
}
public function __construct(Main $main)
{
$this->main = $main;
}
}
class B extends Main {
protected $main;
public function getVar()
{
return $this->main->foo;
}
public function __construct(Main $main)
{
$this->main = $main;
}
}
$M = new Main;
$A = new A($M);
$B = new B($M);
$A->setVar("test");
echo "result: ".$B->getVar();
To further clarify things, it may help to think it in this way. Let's say your Main is a class that stands for a vehicle (and hence we rename it Vehicle), so class means that Vehicle defines the characteristics of a veicle but not a particular one to do operations with (we need an instance for that).
Let's translate also your class A in Car and B in Truck: therefore they are specializations of your main class that, along with possessing every characteristic of a generic Vehicle, extend the concept of "vehicle" and point out specific behaviours and properties of Cars in general, and Trucks in general, but - and that's the point - don't reference a particular car or truck. We need instances for that.
class Vehicle
{
public $numberOfTires;
}
class Car extends Vehicle
{
function smashMe()
{
echo "Oops!";
}
}
class Truck extends Vehicle
{
function smashMe()
{
echo "More Oops!";
}
}
From this point we can define particular instances (your Porsche, my Camaro... different cars) on which we may perform operations (calling methods) and set properties.
$genericVehicle = new Vehicle;
$myPorsche = new Car;
$yourCamaro = new Car;
$hisTruck = new Truck;
$herTruck = new Truck;
But every instance remains independent from the other.
Both are different instances of the the same class.
Operations done on one instance, does not apply on any of the others (That's kinda the point of OOP).
To do what you want, You can have B extend A, and then perform both operations on a single instance of the B class.

Categories