I haven't found an exact duplicate of the question I'm posting here, so here it is:
class CBaseEntity
{
public function __construct()
{
}
public function __get($property)
{
$property = "m_".$property;
echo $property;
if(property_exists($this, $property))
{
$result = $this->$property;
return $result;
}
else
{
throw new Exception("Property not found $property");
}
}
}
class CCategoryEntity extends CBaseEntity
{
private $m_id;
private $m_name;
private $m_description;
public function __construct()
{
}
public static function CreateFromParams($id,$name, $description)
{
$instance = new CCategoryEntity();
$instance->m_id=$id;
$instance->m_name=$name;
$instance->m_description=$description;
return $instance;
}
public static function CreateFromArray(array $catData)
{
$instance = new CCategoryEntity();
$instance->m_id = $catData["id"];
$instance->m_name = $catData["name"];
$instance->m_description = $catData["description"];
return $instance;
}
}
$categoryEntity = CCategoryEntity::CreateFromParams(3, "EntityA", "Entity");
echo $categoryEntity->name;
Fatal error: Uncaught exception 'Exception' with message 'Property not found m_m_name'
Exception: Property not found m_m_name in ***\models\BaseModel.php on line 33
It appears that the name property is resolved via the get magic method, but the return in the body of this getter will again issue a call to itself instead of, somehow, intelligently stopping as the string in $property coincides with the name of the sought attribute. I could do a string compare and see if the property starts with m_, but I don't want a second call being issued already in this case.
There are other SO questions involving similar code that does not yield unnecessary recursion. Did something change in php or is this the default behaviour? What can be done to reach the goal intended by this design in an elegant way?
Recursion appears because you are checking only if property exists, not if it is accessible from current scope. Since it is declared private, it is not accessible from parent or child classes, only in class where it was declared, thats why __get is called second time (first was for non-existent property, now it is for inaccessible one). Try out get_object_vars to get accessible properties.
You may want to make it protected, or define public getter methods for private properties.
I don't know what you mean but there's no second call. When you access the property outside the class, __get() will be called just once. $this->$property inside the __get() method does not trigger another call.
From the PHP documentation:
The overloading methods are invoked when interacting with properties
or methods that have not been declared or are not visible in the
current scope.
this means that the __get() method is called only when there is no property available for that call.
Related
What I'm trying to do is the following:
class A {
public function __construct($a) {
// stuff here
}
public static function request() {
$instance = new self("hi");
$instance->bye(); // this weirdly only sometimes throws errors
}
private function bye() {
// stuff here
}
}
A::request();
The line of interest is $instance->bye() - is this allowed within the static context in a way but when called on an instance and inside the same class as the constructor? Or is this not a good practice in general? It's strange that this only throws errors sometimes of calling on a private method with no context.
Any help appreciated!
Turns out this is fine and PHPStorm debugger was creating the issue and screwing up the context hence why the error only happened sometimes which apparently happens on our systems ¯_(ツ)_/¯
Calling a regular method from a static method should never be done.
As there may be no instance at all (Ex: Calling A::request() directly), it will throw an error.
A static method will be the same for all instances of the class, so it won't be able to access non-static properties.
What could but shouldn't be done is calling an object of the same type private method from a static method:
<?php
class A {
private function foo()
{
print("bar");
}
static public function bar($a)
{
$a->foo();
}
}
$a = new A();
A::bar($a);
?>
From http://php.net/manual/en/language.oop5.visibility.php
This question is related to this other question: PHP's magic method __call on subclasses, but I'm not satisfied with the accepted answer.
What I'm trying to do is implement a generic way to create method aliases, without having to define a named function for every alias, using the magic __call method.
This system would use an associative array as a lookup table in the form of "alias" => "actualMethod.
abstract class Super {
private $aliases;
protected function __construct(array $aliases) {
$this->aliases = $aliases;
}
public function __call($name, $arguments) {
/* if $name is an alias, replace it */
if (isset($this->aliases[$name])) {
$name = $this->aliases[$name];
}
/* throw an exception if the method is undefined */
if (!method_exists($this, $name)) {
throw new Exception("The specified method or method alias is undefined in the current context");
}
/* finally, call the method by its actual name */
return $this->$name($arguments);
}
}
The problem seems to be that either me or the PHP guys don't understand polymorphism.
class Sub extends Super {
public function __construct() {
parent::__construct(array(
"alias" => "actualMethod"
));
}
private function actualMethod() {
echo "Inside the actual method";
}
}
When I define the __call method on an abstract class, then define the actualMethod on a subclass, PHP enters an infinite recursion loop inside __call when I try to invoke the actualMethod by its alias.
try {
$object = new Sub();
$object->alias(); /* causes infinite __call recursion inside Super */
} catch (Exception $exc) {
echo $exc->getTraceAsString();
}
This is funny, because the call to method_exists inside __call returns TRUE.
Surely I can't be the first person to notice this behavior, right? What's the deal here?
EDIT
So basically, normal inheritance rules don't apply for magic methods? It seems that I can't call private methods further down the inheritance tree from inside __call() (*). However I can still call private methods if they are defined in the same class.
(*): even though __call is public, and the object is an instance of the subclass where the private method is defined.
How does that work exactly?
Yes, this is weird - I don't have an answer to why, but a workaround for your problem could be:
/* finally, call the method by its actual name */
return call_user_func_array(array($this, $name), $arguments);
Looks like I found a way to do it. I'm not sure if it is the way to do it or just a dirty hack. Anyway:
class Sub extends Super {
public function __construct() {
parent::__construct(array(
"alias" => "actualMethod"
));
}
public function __call($name, $arguments) {
if (!method_exists($this, $name)) {
return parent::__call($name, $arguments);
}
return $this->$name($arguments);
}
private function actualMethod() {
echo "Inside the actual method";
}
}
This works by only calling the __call method inside Sub if the specified method does not exist already in either Sub or Super. When it doesn't, the Sub::__call() is invoked, which in turn invokes Super::__call. The result is that either an exception is thrown, or control is handed back to Sub::__call which then invokes the actualMethod().
I hope that made sense.
EDIT
I completely forgot to add return keywords in my examples. Obviously these are crucial if you're trying to return anything other than void.
My team is using lazyloading techniques to load sub-objects from our database. To do this we're using the magic __get() method and making a database call. All of our properties are protected so the __get method gets called from outside of the object but our issue is that it doesn't get called from within the object without using $this->__get($name);
So my question is: Is it possible to force __get() to be called with the normal chaining operator even from within the object?
If I want to chain object access I currently have to do:
$this->__get('subObject')->__get('subObject')->__get('subObject')
Is it possible to write the following, but have it still call __get()?
$this->subObject->subObject->subObject
Thanks,
Jordan
Jordan,
PHP won't call the __get() method unless the property is inaccessible—either because it doesn't exist, or because of visibility. When the property is hidden from the calling scope the __get() method is called because the property is inaccessible. When the property is referenced and is available to the calling scope, __get() will never fire. (It's not meant to fire for an existing/accessible property.)
To work around this you have to rename your internal properties. Either prefix them with a name, underscore, or store them in a common array parameter with a special name. The following is somewhat contrived, but it ought to demonstrate how you can deal with this situation.
class MyObject {
protected $lazyloads = array();
protected function lazyload($relation) {
// ...
}
public function __get($key) {
if (isset($this->lazyloads[$key]) {
return $this->lazyloads[$key];
} else {
return $this->lazyload($key);
}
}
}
See: http://www.php.net/manual/language.oop5.overloading.php#object.get
#Robert K Or (for example):
class A
{
protected $objects = array();
public function __get($name)
{
if(isset($this->objects[$name])) {
return $this->objects[$name];
}
return null; // or throw \Exception
}
}
class B
{
protected $objects = array();
public function __get($name)
{
// for example: $this->objects[$name] is type of A
if(isset($this->objects[$name])) {
return $this->objects[$name];
}
return null; // or throw \Exception
}
}
each returned object must have a magic method __get that returns an object.
I have the following class in PHP
class MyClass
{
// How to declare MyMember here? It needs to be private
public static function MyFunction()
{
// How to access MyMember here?
}
}
I am totally confused about which syntax to use
$MyMember = 0; and echo $MyMember
or
private $MyMember = 0; and echo $MyMember
or
$this->MyMember = 0; and echo $this->MyMember
Can someone tell me how to do it?
I am kind of not strong in OOPS.
Can you do it in the first place?
If not, how should I declare the member so that I can access it inside static functions?
class MyClass
{
private static $MyMember = 99;
public static function MyFunction()
{
echo self::$MyMember;
}
}
MyClass::MyFunction();
see Visibility and Scope Resolution Operator (::) in the oop5 chapter of the php manual.
This is a super late response but it may help someone..
class MyClass
{
private $MyMember;
public static function MyFunction($class)
{
$class->MyMember = 0;
}
}
That works. You can access the private member that way, but if you had $class you should just make MyFunction a method of the class, as you would just call $class->MyFunction(). However you could have a static array that each instance is added to in the class constructor which this static function could access and iterate through, updating all the instances. ie..
class MyClass
{
private $MyMember;
private static $MyClasses;
public function __construct()
{
MyClass::$MyClasses[] = $this;
}
public static function MyFunction()
{
foreach(MyClass::$MyClasses as $class)
{
$class->MyMember = 0;
}
}
}
Within static methods, you can't call variable using $this because static methods are called outside an "instance context".
It is clearly stated in the PHP doc.
<?php
class MyClass
{
// A)
// private $MyMember = 0;
// B)
private static $MyMember = 0;
public static function MyFunction()
{
// using A) // Fatal error: Access to undeclared static property:
// MyClass::$MyMember
// echo MyClass::$MyMember;
// using A) // Fatal error: Using $this when not in object context
// echo $this->MyMember;
// using A) or B)
// echo $MyMember; // local scope
// correct, B)
echo MyClass::$MyMember;
}
}
$m = new MyClass;
echo $m->MyFunction();
// or better ...
MyClass::MyFunction();
?>
Static or non-static?
Did you ever asked yourself this question?
You can not access non static parameters / methods from inside
static method (at least not without using dependency injection)
You can however access static properties and methods from with in non-static method (with self::)
Properties
Does particular property value is assign to class blueprint or rather to it instance (created object from a class)?
If the value is not tight to class instance (class object) then you could declare it as as static property.
private static $objectCreatedCount; // this property is assign to class blueprint
private $objectId; // this property is assign explicitly to class instance
Methods
When deciding on making a method static or non-static you need to ask yourself a simple question. Does this method need to use $this? If it does, then it should not be declared as static.
And just because you don't need the $this keyword does not
automatically mean that you should make something static (though the
opposite is true: if you need $this, make it non-static).
Are you calling this method on one individual object or on the class in general? If you not sure which one to use because both are appropriate for particular use case, then always use non-static. It will give you more flexibility in future.
Good practice is to always start to design your class as non-static and force static if particular us case become very clear.
You could try to declare your parameters as static... just so you can access it from static method but that usually is not what you want to do.
So if you really need to access $this from static method then it means that you need to rethink/redesign your class architecture because you have don it wrong.
I have a variable on the global scope that is named ${SYSTEM}, where SYSTEM is a defined constant. I've got a lot of classes with functions that need to have access to this variable and I'm finding it annoying declaring global ${SYSTEM}; every single time.
I tried declaring a class variable: public ${SYSTEM} = $GLOBALS[SYSTEM]; but this results in a syntax error which is weird because I have another class that declares class variables in this manner and seems to work fine. The only thing I can think of is that the constant isn't being recognised.
I have managed to pull this off with a constructor but I'm looking for a simpler solution before resorting to that.
EDIT
The global ${SYSTEM} variable is an array with a lot of other child arrays in it. Unfortunately there doesn't seem to be a way to get around using a constructor...
Ok, hopefully I've got the gist of what you're trying to achieve
<?php
// the global array you want to access
$GLOBALS['uname'] = array('kernel-name' => 'Linux', 'kernel-release' => '2.6.27-11-generic', 'machine' => 'i686');
// the defined constant used to reference the global var
define(_SYSTEM_, 'uname');
class Foo {
// a method where you'd liked to access the global var
public function bar() {
print_r($this->{_SYSTEM_});
}
// the magic happens here using php5 overloading
public function __get($d) {
return $GLOBALS[$d];
}
}
$foo = new Foo;
$foo->bar();
?>
This is how I access things globally without global.
class exampleGetInstance
{
private static $instance;
public $value1;
public $value2;
private function initialize()
{
$this->value1 = 'test value';
$this->value2 = 'test value2';
}
public function getInstance()
{
if (!isset(self::$instance))
{
$class = __CLASS__;
self::$instance = new $class();
self::$instance->initialize();
}
return self::$instance;
}
}
$myInstance = exampleGetInstance::getInstance();
echo $myInstance->value1;
$myInstance is now a reference to the instance of exampleGetInstance class.
Fixed formatting
You could use a constructor like this:
class Myclass {
public $classvar;
function Myclass() {
$this->classvar = $GLOBALS[SYSTEM];
}
}
EDIT: Thanks for pointing out the typo, Peter!
This works for array too. If assignment is not desired, taking the reference also works:
$this->classvar =& $GLOBALS[SYSTEM];
EDIT2: The following code was used to test this method and it worked on my system:
<?php
define('MYCONST', 'varname');
$varname = array("This is varname", "and array?");
class Myclass {
public $classvar;
function Myclass() {
$this->classvar =& $GLOBALS[MYCONST];
}
function printvar() {
echo $this->classvar[0];
echo $this->classvar[1];
}
};
$myobj = new Myclass;
$myobj->printvar();
?>
The direct specification of member variables can not contain any references to other variables (class {public $membervar = $outsidevar;} is invalid as well). Use a constructor instead.
However, as you are dealing with a constant, why don't you use php's constant or class constant facilities?
You're trying to do something really out-of-the-ordinary here, so you can expect it to be awkward. Working with globals is never pleasant, especially not with your dynamic name selection using SYSTEM constant. Personally I'd recommend you use $GLOBALS[SYSTEM] everywhere instead, or ...
$sys = $GLOBALS[SYSTEM];
... if you're going to use it alot.
You could also try the singleton pattern, although to some degree it is frowned upon in OOP circles, it is commonly referred to as the global variable of classes.
<?php
class Singleton {
// object instance
private static $instance;
// The protected construct prevents instantiating the class externally. The construct can be
// empty, or it can contain additional instructions...
protected function __construct() {
...
}
// The clone and wakeup methods prevents external instantiation of copies of the Singleton class,
// thus eliminating the possibility of duplicate objects. The methods can be empty, or
// can contain additional code (most probably generating error messages in response
// to attempts to call).
public function __clone() {
trigger_error('Clone is not allowed.', E_USER_ERROR);
}
public function __wakeup() {
trigger_error('Deserializing is not allowed.', E_USER_ERROR);
}
//This method must be static, and must return an instance of the object if the object
//does not already exist.
public static function getInstance() {
if (!self::$instance instanceof self) {
self::$instance = new self;
}
return self::$instance;
}
//One or more public methods that grant access to the Singleton object, and its private
//methods and properties via accessor methods.
public function GetSystemVar() {
...
}
}
//usage
Singleton::getInstance()->GetSystemVar();
?>
This example is slightly modified from wikipedia, but you can get the idea. Try googling the singleton pattern for more information
I'd say the first two things that stand out to me are:
You don't need the brackets around the variable name, you can simply do public $system or public $SYSTEM.
While PHP may not always require it it is standard practice to encapsulate non-numeric array indexes in single or double quotes in case the string you're using becomes a constant at some point.
This should be what you're looking for
class SomeClass {
public $system = $GLOBALS['system'];
}
You can also use class constants which would instead be
class SomeClass {
const SYSTEM = $GLOBALS['system'];
}
This can be referenced within the class with 'self::SYSTEM' and externally with 'SomeClass::SYSTEM'.