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.
Related
I am new to php.I know __set() and __get() create protected property but it behaves like a public property.And properties created by this method are set to PROTECTED.But only difference is that they can be accessed any time from anywhere just like public properties.As i don't have any practical working experience on php,I would like to know why not create a public property instead of taking the trouble of using __get() and __set()??Also __set() property creates property during runtime.Does it create a problem while tracking all the properties of an object??
class foo {
protected $another='lol'; //protected property
public function __get($name) {
return $this->$name;
}
public function __set($name, $value) {
$this->$name = $value;
}
}
class bar extends foo{ // inherits from foo
public function __get($name){ //__get() method for bar class
return $this->$name; //
}
}
$foo = new foo();
$foo->bar = 'test';
echo $foo->another; //echos protected property from parent class
echo '</br>';
$bar=new bar();
echo $bar->another; // echos inherited private property from parent class
var_dump($foo);
It's all to do with encapsulating the data in a class so that the outer world cannot directly modify the values of this data. If you're repeatedly setting the values of a variable from outside class you might want to think about if the variable you're changing should actually be in its current class. You can also have more control over the variable's accessibility. For example, just providing a get() method and preventing yourself from setting a value when you shouldn't really be doing it. Having a method to set the value of something also provides a very handy place for validation rather than checking values outside the class where you may forget from time to time. Also protected properties are different from public properties as they can't be accessed anywhere, just in the variable's own class or classes inherited from the class the variable is in.
There's very little reason to use __get, __set (or __call for that matter) in a way where the actual data structure is fixed (e.g. you have a fixed set of members and only access them through those methods.
The advantage of these methods lies in situations where you don't actually have a fixed structure. While these situations should usually be avoided there are some situations where this may become handy.
For instance I have a model class for a very lightweight ORM which doesn't require code generation and still features a public interface similar to more complex ActiveRecord style frameworks (i use __call in this and extract the field name from the called method, but __get/__set would work as well).
class User extends AbstractModel {
protected static $FIELD_LIST = ['id', 'username', 'password'];
}
$foo = new MyModel();
$foo->setId(123);
$foo->setUsername('Foo');
$foo->setPassword('secret');
$foo->setNonExistantField('World!'); // will throw an exception
This allows me to rapidly create a model class where at any point I can decide to write a custom setter method. e.g. if I wanted to store that password as a salted hash I could do something like this:
class User extends AbstractModel {
protected static $FIELD_LIST = ['id', 'username', 'password'];
public function setPassword($password) {
$salt = magic_salt_function();
$hash = crypt($password, '$2a$08$' . $salt);
$this->data['password'] = $hash;
}
}
The advantage being that I don't have to write getter/setter methods for every field but at any point can. Very handy in rapid prototyping.
Similar techniques can be used for example if you have some data in array from which you want to modify with object syntax. Using __get/__set allows you to avoid having to go through the array whenever you leave object context back to array context.
class Foo {
protected $data;
public function __construct(array $data) {
$this->data = $data;
}
public function __get($key) {
if(!isset($this->data[$key])) {
throw new Exception("Unknown member $key");
}
return $this->data[$key];
}
public function __set($key, $value) {
if(!isset($this->data[$key])) {
throw new Exception("Unknown member $key");
}
$this->data[$key] = $value;
}
public function getData() {
return $this->data;
}
}
$data = [
'bar' => true,
'braz' => false
];
$foo = new Foo($data);
$foo->bar = false;
$foo->braz = true;
$foo->nope = true; // will throw an exception
In the end overloading in PHP is a tool for a pretty specific task (creating dynamic interfaces). If you don't need it, you don't use it. And when you use it, you should be aware that it has its downsides. After all once you overload you're in charge of validation that normally the interpreter could do for you.
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.
I've seen this use of syntax a few times on the Yii framework. I tried searching for an explanation but got no examples. A link will be nice if possible. It goes something like class::model()->function();
My understanding is that model is an object of class therefore it can access the function. So I tried to code it but I get " Call to a member function sound() on a non-object in". Here's my code
class animal
{
private static $obj;
public static function obj($className = __CLASS__)
{
return self::$obj;
}
public static function walk()
{
return "walking";
}
}
include('animal.php');
class cat extends animal
{
public static function obj($className = __CLASS__)
{
return parent::obj($className);
}
public static function sound()
{
return "meow";
}
}
echo cat::obj()->sound();
Also what benefits does it have?
That is called an object operator and this, ->, calls a class method from your created object which is defined in that class.
Here is an explanation and some examples.
$obj = new Class; // Object of the class
$obj->classMethod(); // Calling a method from that class with the object
echo cat::obj()->sound();
This will display the output of the sound() method, called on the object that is returned from cat::obj().
The reason it's failing for you is because cat::obj() is not returning a valid object.
And the reason for that is because the obj() method returns the static obj property, but you're not actually setting that obj property anywhere.
The pattern you're trying to use here is known as a "Singleton" object. In this pattern, you call an obj() method to get the single instance of the class; every call to the method will give you the same object.
However the first call to the method needs to instantiate the object; this is what you're missing.
public static function obj($className = __CLASS__){
if(!static::$obj) {static::$obj = new static;}
return static::$obj;
}
See the new line where the object is created if it doesn't exist.
Also note that I've changed self to static. The way you're using this with class inheritance means that you probably expect to have a different static object for each class type, self will always return the root animal::$obj property, whereas static will return the $obj property for whichever class you're calling it from.
There are a few other bugs you need to watch out for too. For example, you've defined the sound() method as static, but you're calling it with ->, so it shouldn't be static.
Hope that helps.
The cat::obj() returns you a object of the type cat. With ->sound(); you're executing the function sound() from the class cat. All that should return "meow".
cat::obj() returns an object; ->sound(); execute a method of this object. Equivalent is
$o = cat::obj();
$o->sound();
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.
Why can't bee() call bar() unless I prepend self:: ?
class X {
function bar ()
{
echo "OK";
}
public static function bee ()
{
bar ();
}
};
$x = new X ();
$x->bee ();
static functions do not have access to the $this pointer, but what you have written there is actually trying to call the global function bar(). A regular call to a method on $x would be something like:
class X
{
...
static function Bee()
{
$this->Bar();
}
}
but this is not good practice because then your static function depends on being called from an object and there is no point in having it be static.
http://en.wikipedia.org/wiki/Method_(computer_programming)#Static_methods
As mentioned above, a method may be
declared as static, meaning that it
acts at the class level rather than at
the instance level. Therefore, a
static method cannot refer to a
specific instance of the class (i.e.
it cannot refer to this, self, Me,
etc.), unless such references are made
through a parameter referencing an
instance of the class, although in
such cases they must be accessed
through the parameter's identifier
instead of this. Most importantly
there is no need to make an object for
accessing data .i.e. without creating
an object we can access the data
members of a static class.
you are insight of a static method.
Inside of a static method you are not in the object context so you cannot call the method with $this, ...
self::bar() or X::bar (); does the job...
But be carefull. Stuff like that is not possible if you call it from a static method:
function bar ()
{
echo $this->test;
}
I would not make the method static unless you need it!
BR,
TJ
Static methods get called via the scope resolution operator ::
X::bee();
Then inside bee() you call bar(), what is a regular function, not a method. Here you must call it the same way as mentioned above, but you can simplify the scope identifier to self
public static bee () {
self::bar();
}
Just to remember: Every "function" inside a class is in real a method. If you omit the visibility identifier public is implicitly assumed, but it will remain a method and must be called like this.
I see bar() is not static. To call a regular method you need an instance of the object within the context you are going to call it. Here you can either make bee() non-static (then $this as reference to the current object is avaible), or you instanciate a new object within bee().
class X {
public function bar () { /* .. */ }
public function bee () { $this->bar(); }
}
$x = new X;
$x->bee();
or
class X {
public function bar () { /* .. */ }
public static function bee () {
$x = new self();
$x->bar();
}
}
X:bee();
Whats better depends on what the methods should do (meaning: Whats the design of the class in whole):