Grabbing extra (chained) method calls into __get() magic-method - php

I don't have high hopes for this one:
Given a service that has an action like the foo in $service->foo->bar( $a, $b ) and a container class that has $service as a protected property, is there a way to use __get() on the container to access the service and pass along the ->bar( $a, $b )?
class Container {
protected $service;
protected $responses = [];
public function __get ($name) {
$this->responses[] = $service->$name-> _???_
return $resp;
}
}
that could be intercepted so that the Container could catch the operation, call $service with it, save the response, then return the response
Either some way to grab whatever else is being chained on the $name or if $name just included all of that. I know this is a little convoluted, but it would help me clean some code up substantially, plus I'm just generally curious now.
This app is running PHP7 if that matters.
And alternately, if there is a way to do this but it isn't __get(), insight would be appreciated

The call to bar takes place after __get returned. __get is called by the engine so you can't add additional parameters. However you can create additional methods on your container class to implement the feature you want.
Assuming I understood your question correctly this would result in something like this:
public function executeOnService(string $name, string $method, ...$args) {
return call_user_func_array([$this->service->$name, $method], $args);
}

Since you already expect the consumer code of your Container to know a lot about the properties of this service object, perhaps you should just consider returning the property of the service that the consuming code wants to act upon, e.g.:
public function __get ($name) {
return $service->$name;
}

Related

Reasons for basic __get implementation

Other similar questions:
Why I should use __get() and __set()-magic methods in php?
When do/should I use __construct(), __get(), __set(), and __call() in PHP?
First, I completely understand how to implement __get and __set methods in PHP and have come across scenarios where using these methods is beneficial.
However, I have encountered, and written, code that looks like the following:
class SomeObject {
protected $data = array();
public function __set($key, $val) {
$this->data[$key] = $val;
}
public function __get($key) {
return $this->data[$key];
}
}
Why do we need to do this? I have used objects with no __get or __set methods defined and can still add undefined properties, retrieve the values associated with those properties, invoke isset() and unset() on those properties.
Do we really need to implement these magic methods, using code like above, when objects in PHP already exhibit similar behavior?
Note, I'm not referring to when more code is used in the __set and __get methods for special handling of the data requested but very basic implementation like the code above.
If you are using them just as a glorified wrapper over an array (i.e. absolutely no extra logic), I don't believe they offer any benefit. They do offer a drawback however, in that you need to write the code for them. And that code may be buggy or incomplete.
Did you leave out __isset for brevity? Because if you do use __get and __set but do not provide an __isset, you 've just written yourself a bug.
See what I did there?
In the example you gave, it's very easy to overwrite $data with a new array. If you were dealing with properties, you would have to iterate over an array and perform $this->$key = $data; (to which you may have data leakage).
In more detail:
protected function setData($arr)
{
$this->data = $arr;
}
vs.
protected function setData($arr)
{
foreach($arr as $key => $data)
{
$this->$key => $data;
}
}
With the example above, keys which don't appear in $arr in the current call, but have in previous calls, will not be overwritten, which can lead to undesired results.
In the end, it just depends on what makes the most sense for you.
As a side note, in the example code you gave, you should be sure you're implementing __isset() when you have __get(). If you don't, you'll get unexpected results when using functions like isset() or empty().
To me the sample code looks like "We want all the downsides of an object combined with all the misuse potential of an array"
The only reason I can see for going for something like that is when you are starting to refactor from arrays into objects.
You replace all [''] access with -> but you don't break anything once it compiles again.
In the next step you can start adding rules for specific properties.
public function __set($key, $val) {
if($key == "money") { throw new Exception("Can't touch this"); }
$this->data[$key] = $val;
}
public function __get($key, $val) {
if($key == "money") { return $this->x * $this->y; }
$this->data[$key] = $val;
}
But in general I don't see any use going in that direction. Creating real "value objects" for OO data structures seems a lot more useful.
Doing something like that just for the sake of "I like -> better than [''] isn't a valid use case in my book."
For most use cases with "Objects as data structures" I'd much rather have:
class SomeObject {
protected $a, $b, $c;
public function __construct(TypeA $a, TypeB $b, TypeC $c) {
$this->a = $a;
$this->b = $b;
$this->c = $c;
}
/** #return TypeA */
public function getA($key) {
return $this->a;
}
// an so on
}
That documents the expected values. Works with native type hinting without the need of writing that your self and to me is what I'd expect to get as a "data structure".
If there is a good reason that a class should act like an array then ArrayAccess seems like a much better choice then that kind of magic to me.
There are quite a few different reasons you might want to use this, it all depends on how you want to control your class. A couple of examples:
1.) You have some attributes on your object that are related to a key -> value database. If more values are added, you would like to update the database with these. So in this method, you would have some sort of flag that indicates there is a $new_data array that needs to be synchronized with the database
2.) You want to strictly enforce that an object will only have the variables attached to it that you want to be there. If one doesn't exist for it, this can throw an exception.
3.) You've abstracted a database such that gets/sets will have an immediate impact on their corresponding values in it.
And many others.
In your example, no, you don't.
However, when we go into more complicated classes, you may want to define setters and getters. A sample __set would then be :
function __set($key, $val) {
if (method_exists($this, 'set'.ucfirst($key))) {
$this -> {'set'.ucfirst($key)}($val);
} else {
$this -> data[$key] = $val;
}
}
With this code you can simply define a function setTestKey which will set $this->data['testKey'] to the value in $value, and call it via $storage->testKey = 1;
I'm with you, I've never been a fan of magic methods because I feel that it breaks encapsulation. If you allow calling code to dictate object behavior that the object isn't aware of you risk un-expected behavior, which is harder to track down compared to having static setters/getters accessing defined member variables.
With that being said I have seen some clever instances where magic methods appear to be useful. One instance is calling a logger and defining the loglevel in which you want the message to appear.
Consider the following (I use this in my production code):
// Only works with php 5.3 or greater (uses __callstatic)
class MyCustom_Api_Log
{
/**
* Wrapper method for getting a symfony logger object
*
* #return object
*/
public static function getLogger()
{
return sfContext::getInstance()->getLogger();
}
/**
* Magic method for logging all supported log levels
*
* #param string
* #param array
*/
public static function __callStatic($level, $params)
{
// Only concerned with message at this time
// However any number of params can be passed in
$message = shift($params);
self::getLogger()->$level($message);
}
}
// All these calls are made to __callStatic, so no need for individual methods
MyCustom_Api_Log::err(array('Oh noes something went wrong'));
MyCustom_Api_Log::warn(array('Oh noes something went wrong'));
MyCustom_Api_Log::notice(array('Oh noes something went wrong'));
As you can see, I can call the magic method with err, warn, or notice and logs my messages at that log level.

PHP ReflectionClass hasMethod and __call()

I'm creating a dynamic class that responds to magic __call() method. The problem is, since I'm building this on top of a already existing framework (Kohana), it checks if the method of the class exists using ReflectionClass::hasMethod, and it doesn't seem to trigger the __call() magic method for checking for it's existance. What could I do in this case? Seems like if you add the method dynamically (like $this->{$name} = function(){}) it still can't "see" it
Without more details, I'm unsure if this would suffice, however you could create proxy class to perform intermediate functionality:
class MyProxy {
protected $_object = null;
protected $_methods = array();
public function __construct($object) {
if (!is_object($object)) {
throw new InvalidArgumentException('$object must be an object');
}
$this->_object = $object;
}
public function __call($name, $arguments) {
return $this->callMethod($name, $arguments);
}
public function setMethod($name, Closure $method) {
$this->_methods[(string) $key] = $method;
}
public function callMethod($name, array $arguments) {
if (isset($this->_methods[$name])) {
return call_user_func_array($this->_methods[$name], $arguments);
}
return call_user_func_array(array($this->_object, $name), $arguments);
}
}
By calling $proxy->setMethod('foo', function () { });, you can dynamically "attach" methods to the object. When you call $proxy->foo(), it'll first do a look-up against the dynamically attached methods; if it finds one, it'll call it. Otherwise, it'll just delegate to the internal object.
Now, the problem with this approach is that attached methods aren't bound to the proxy. In other words, $this doesn't exist in the scope of an attached method.
This can be fixed though, with features from PHP 5.4+.
public function setMethod($name, Closure $method) {
$this->_methods[(string) $name] = Closure::bind($method, $this);
}
We've refined setMethod to rebind the passed closure to the proxy. Now, in the scope of an attached method, $this will point to the proxy object.
We could have rebound it to the enclosed object, but then the attached methods couldn't talk to the proxy (or other attached methods). For completeness, you'll want to add __get and __set magic, to forward property access/mutate calls to the internal object (or handle them in the proxy, whatever)
Besides, the internal object has no clue about the proxy, so it's methods (from the class definition) won't know about any of this dynamic magic anyway.
Seems like if you add the method dynamically (like $this->{$name} = function(){}) it still can't "see" it
Right, as you're creating a new property, not a method. At time of writing, PHP did not support calling anonymous functions in properties without going through __call, which isn't Reflection-friendly. Properties-with-anonymous-functions-as-methods work properly in PHP 5.4.
The only other way to add a method to a class in a way that Reflection will pick up on is using the highly experimental "runkit" extension.

Get and set (private) property in PHP as in C# without using getter setter magic method overloading

Summary
Code sample:
Class People {
// private property.
private $name;
// other methods not shown for simplicity.
}
Straight forward. Let me assume that $name is a PRIVATE class member (or property, variable, field, call it as you wish). Is there any way to do these in PHP:
$someone = new People();
$someone->name = $value;
$somevar = $someone->name;
WITHOUT using __get($name) and __set($name, $value).
Background
I needed to check the assigned $value, therefore I simply need a getter setter like this:
getName();
setName($value);
And NOT necessarily a getter setter magic method overloading like this:
__get($value);
__set($value, $name);
That said, I simply need a getter setter. But that's NOT what I want. It just doesn't feel like object oriented, for people from static typed language such as C++ or C# might feel the same way as I do.
Is there any way to get and set a private property in PHP as in C# without using getter setter magic method overloading?
Update
Why Not Magic Method?
There are rumors floating around the web that magic method is 10x slower then explicit getter setter method, I haven't tested it yet, but it's a good thing to keep in mind. (Figured out that it's not that slow, just 2x slower, see the benchmark result below)
I have to stuff everything in one huge method if I use magic method rather then split them into different function for each property as in explicit getter setter. (This requirement might have been answered by ircmaxell)
Performance Overhead Benchmarking
I'm curious about performance overhead between using magic method and explicit getter setter, therefore I created my own benchmark for both method and hopefully it can be useful to anyone read this.
With magic method and method_exist:
(click here to see the code)
Getter costs 0.0004730224609375 second.
Setter costs 0.00014305114746094 second.
With explicit getter setter:
(click here to see the code)
Getter costs 0.00020718574523926 second.
Setter costs 7.9870223999023E-5 second (that's 0.00007xxx).
That said, both setter and getter with magic method and method exists justs costs 2x than the explicit getter setter. I think it's still acceptable to use it for a small and medium scale system.
Nope.
However what's wrong with using __get and __set that act as dynamic proxies to getName() and setName($val) respectively? Something like:
public function __get($name) {
if (method_exists($this, 'get'.$name)) {
$method = 'get' . $name;
return $this->$method();
} else {
throw new OutOfBoundsException('Member is not gettable');
}
}
That way you're not stuffing everything into one monster method, but you still can use $foo->bar = 'baz'; syntax with private/protected member variables...
ReflectionClass is your salvation
I know it's too late for Hendra but i'm sure it will be helpfull for many others.
In PHP core we have a class named ReflectionClass wich can manipulate everything in an object scope including visibility of properties and methods.
It is in my opinion one of the best classes ever in PHP.
Let me show an example:
If you have an object with a private property and u want to modify it from outside
$reflection = new ReflectionClass($objectOfYourClass);
$prop = $reflection->getProperty("PrivatePropertyName");
$prop->setAccessible(true);
$prop->setValue($objectOfYourClass, "SOME VALUE");
$varFoo = $prop->getValue();
This same thing you can do with methods eighter;
I hope i could help;
If using magical properties doesn't seem right then, as already pointed out by other posters, you can also consider ReflectionClass::getProperty and ReflectionProperty::setAccessible.
Or implement the necessary getter and setter methods on the class itself.
In response to the language features issue that you raised, I'd say that having a dynamically typed language differ from a statically typed one is expected. Every programming language that has OOP implements it somewhat differently: Object-Oriented Languages: A Comparison.
class conf_server
{
private $m_servidor="localhost";
private $m_usuario = "luis";
private $m_contrasena = "luis";
private $m_basededatos = "database";
public function getServer(){
return $this->m_servidor;
}
public function setServer($server){
$this->m_servidor=$server;
}
public function getUsuario(){
return $this->m_usuario;
}
public function setUsuario($user){
$this->m_usuario=$user;
}
public function getContrasena(){
return $this->m_contrasena;
}
public function setContrasena($password){
$this->m_contrasena=$password;
}
public function getBaseDatos(){
return $this->m_basededatos;
}
public function setBaseDatos($database){
$this->m_basededatos->$database;
}
}

Is there a way to reassign $this?

First of all, I do not want to extend a class. I would ideally like to do this.
public function __construct() {
/* Set Framework Variable */
global $Five;
$this =& $Five;
}
I have a system where the variable $Five is a container class which contains other libraries. I could assign this to a local variable of Five... i.e.
public function __construct() {
/* Set Framework Variable */
global $Five;
$this->Five = $Five;
}
However, the reason why I am trying to avoid this is that function calls would be getting a little long.
$this->Five->load->library('library_name');
Its a little ugly. Far better would be.
$this->load->library('library_name');
What is the best solution for this?
I think that
$this->Five->load->library('library_name');
is going to be your best option unless you decide to have the class extend the helper class. AKA
class Something extends Helper_Class
However, this means that Helper_Class is instantiated every time you instantiate a class.
Another method would be to have a pseudo-static class that assigned all of the helper classes to class members
public function setGlobals($five)
{
$this->loader = $five->loader;
}
Then just call it
public function __construct($five)
{
someClass::setGlobals($five);
}
If $Five is a global, you could just global $Five everytime you want to use it, but putting that at the top of every function just seems like bad coding.
Also, I'd just like to do my public service announcement that Global variables are generally a bad idea, and you might want to search 'Dependency Injection' or alternative to globals. AKA
public function __construct($five);
instead of
global $five;
Globals rely on an outside variable to be present and already set, while dependency injection requests a variable that it is assuming to be an instance of the Five class.
If you are running PHP 5.1 (Thanks Gordon), you can insure the variable is an instance of the FiveClass by doing this:
public function__construct(FiveClass $five);
$this is a reference to the current instance of the class you are defining. I do not believe you can assign to it. If Five is a global you ought to be able to just do this:
$Five->load->library('library_name');
You might wanna go with some kind of implementation of the dependency injection pattern:
Dependency injection (DI) in computer
programming refers to the process of
supplying an external dependency to a
software component. It is a specific
form of inversion of control where the
concern being inverted is the process
of obtaining the needed dependency.
See also the documentation for the symfony DI container. I can highly recommend this DI container implementation if you want to improve the way you handle your 'globals'.
You could also have a read of this question on 'best ways to access global objects'.
How about making the relevant data members and methods of Five static class members? This
$this->Five->load->library('library_name');
would become this
Five::load->library('library_name');
and you wouldn't have to pass &$Five around everywhere.
You cannot overwrite $this (like e.g. in C++) but you can easily build an aggregate using __call() for method calls and __get(), __set(), __isset() for properties.
Example for __call():
class Five {
public function bar() {
echo __METHOD__, " invoked\n";
}
}
class Foo {
protected $Five = null;
public function __construct(Five $five=null) {
if ( is_object($five) ) {
$this->Five = $five;
}
}
public function __call($name, $args) {
// there's no accessible method {$name} in the call context
// let's see if there is one for the object stored in $five
// and if there is, call it.
$ctx = array($this->Five, $name);
if ( !is_null($this->Five) && is_callable($ctx) ) {
return call_user_func_array($ctx, $args);
}
else {
// ....
}
}
}
$foo = new Foo(new Five);
$foo->bar();
prints Five::bar invoked.
In my opinion the biggest draw back is that it is much harder to see "from the outside" what the object is capable of.
I'm pretty sure you can't reassign $this, as it's one of those special things that looks like a variable in PHP, but is treated slightly differently behind the scenes.
If your concerns are the semantics of your method calling getting too long, I'd make load a method call instead of an object property
$this->load()->library('library_name');
public function load()
{
return $this->Five;
}
maybe better for you will be to use PHP Magic Methods?
http://www.php.net/manual/en/language.oop5.overloading.php#language.oop5.overloading.methods

Is this a reasonable way to handle getters/setters in a PHP class?

I'm going to try something with the format of this question and I'm very open to suggestions about a better way to handle it.
I didn't want to just dump a bunch of code in the question so I've posted the code for the class on refactormycode.
base class for easy class property handling
My thought was that people can either post code snippets here or make changes on refactormycode and post links back to their refactorings. I'll make upvotes and accept an answer (assuming there's a clear "winner") based on that.
At any rate, on to the class itself:
I see a lot of debate about getter/setter class methods and is it better to just access simple property variables directly or should every class have explicit get/set methods defined, blah blah blah. I like the idea of having explicit methods in case you have to add more logic later. Then you don't have to modify any code that uses the class. However I hate having a million functions that look like this:
public function getFirstName()
{
return $this->firstName;
}
public function setFirstName($firstName)
{
return $this->firstName;
}
Now I'm sure I'm not the first person to do this (I'm hoping that there's a better way of doing it that someone can suggest to me).
Basically, the PropertyHandler class has a __call magic method. Any methods that come through __call that start with "get" or "set" are then routed to functions that set or retrieve values into an associative array. The key into the array is the name of the calling method after getting or setting. So, if the method coming into __call is "getFirstName", the array key is "FirstName".
I liked using __call because it will automatically take care of the case where the subclass already has a "getFirstName" method defined. My impression (and I may be wrong) is that the __get & __set magic methods don't do that.
So here's an example of how it would work:
class PropTest extends PropertyHandler
{
public function __construct()
{
parent::__construct();
}
}
$props = new PropTest();
$props->setFirstName("Mark");
echo $props->getFirstName();
Notice that PropTest doesn't actually have "setFirstName" or "getFirstName" methods and neither does PropertyHandler. All that's doing is manipulating array values.
The other case would be where your subclass is already extending something else. Since you can't have true multiple inheritances in PHP, you can make your subclass have a PropertyHandler instance as a private variable. You have to add one more function but then things behave in exactly the same way.
class PropTest2
{
private $props;
public function __construct()
{
$this->props = new PropertyHandler();
}
public function __call($method, $arguments)
{
return $this->props->__call($method, $arguments);
}
}
$props2 = new PropTest2();
$props2->setFirstName('Mark');
echo $props2->getFirstName();
Notice how the subclass has a __call method that just passes everything along to the PropertyHandler __call method.
Another good argument against handling getters and setters this way is that it makes it really hard to document.
In fact, it's basically impossible to use any sort of document generation tool since the explicit methods to be don't documented don't exist.
I've pretty much abandoned this approach for now. It was an interesting learning exercise but I think it sacrifices too much clarity.
The way I do it is the following:
class test {
protected $x='';
protected $y='';
function set_y ($y) {
print "specific function set_y\n";
$this->y = $y;
}
function __call($function , $args) {
print "generic function $function\n";
list ($name , $var ) = split ('_' , $function );
if ($name == 'get' && isset($this->$var)) {
return $this->$var;
}
if ($name == 'set' && isset($this->$var)) {
$this->$var= $args[0];
return;
}
trigger_error ("Fatal error: Call to undefined method test::$function()");
}
}
$p = new test();
$p->set_x(20);
$p->set_y(30);
print $p->get_x();
print $p->get_y();
$p->set_z(40);
Which will output (line breaks added for clarity)
generic function set_x
specific function set_y
generic function get_x
20
generic function get_y
30
generic function set_z
Notice: Fatal error: Call to undefined method set_z() in [...] on line 16
#Brian
My problem with this is that adding "more logic later" requires that you add blanket logic that applies to all properties accessed with the getter/setter or that you use if or switch statements to evaluate which property you're accessing so that you can apply specific logic.
That's not quite true. Take my first example:
class PropTest extends PropertyHandler
{
public function __construct()
{
parent::__construct();
}
}
$props = new PropTest();
$props->setFirstName("Mark");
echo $props->getFirstName();
Let's say that I need to add some logic for validating FirstNames. All I have to do is add a setFirstName method to my subclass and that method is automatically used instead.
class PropTest extends PropertyHandler
{
public function __construct()
{
parent::__construct();
}
public function setFirstName($name)
{
if($name == 'Mark')
{
echo "I love you, Mark!";
}
}
}
I'm just not satisfied with the limitations that PHP has when it comes to implicit accessor methods.
I agree completely. I like the Python way of handling this (my implementation is just a clumsy rip-off of it).
Yes that's right the variables have to be manually declared but i find that better since I fear a typo in the setter
$props2->setFristName('Mark');
will auto-generate a new property (FristName instead of FirstName) which will make debugging harder.
I like having methods instead of just using public fields, as well, but my problem with PHP's default implementation (using __get() and __set()) or your custom implementation is that you aren't establishing getters and setters on a per-property basis. My problem with this is that adding "more logic later" requires that you add blanket logic that applies to all properties accessed with the getter/setter or that you use if or switch statements to evaluate which property you're accessing so that you can apply specific logic.
I like your solution, and I applaud you for it--I'm just not satisfied with the limitations that PHP has when it comes to implicit accessor methods.
#Mark
But even your method requires a fresh declaration of the method, and it somewhat takes away the advantage of putting it in a method so that you can add more logic, because to add more logic requires the old-fashioned declaration of the method, anyway. In its default state (which is where it is impressive in what it detects/does), your technique is offering no advantage (in PHP) over public fields. You're restricting access to the field but giving carte blanche through accessor methods that don't have any restrictions of their own. I'm not aware that unchecked explicit accessors offer any advantage over public fields in any language, but people can and should feel free to correct me if I'm wrong.
I've always handled this issue in a similar with a __call which ends up pretty much as boiler plate code in many of my classes. However, it's compact, and uses the reflection classes to only add getters / setters for properties you have already set (won't add new ones). Simply adding the getter / setter explicitly will add more complex functionality. It expects to be
Code looks like this:
/**
* Handles default set and get calls
*/
public function __call($method, $params) {
//did you call get or set
if ( preg_match( "|^[gs]et([A-Z][\w]+)|", $method, $matches ) ) {
//which var?
$var = strtolower($matches[1]);
$r = new ReflectionClass($this);
$properties = $r->getdefaultProperties();
//if it exists
if ( array_key_exists($var,$properties) ) {
//set
if ( 's' == $method[0] ) {
$this->$var = $params[0];
}
//get
elseif ( 'g' == $method[0] ) {
return $this->$var;
}
}
}
}
Adding this to a class where you have declared default properties like:
class MyClass {
public $myvar = null;
}
$test = new MyClass;
$test->setMyvar = "arapaho";
echo $test->getMyvar; //echos arapaho
The reflection class may add something of use to what you were proposing. Neat solution #Mark.
Just recently, I also thought about handling getters and setters the way you suggested (the second approach was my favorite, i.e. the private $props array), but I discarded it for it wouldn't have worked out in my app.
I am working on a rather large SoapServer-based application and the soap interface of PHP 5 injects the values that are transmitted via soap directly into the associated class, without bothering about existing or non-existing properties in the class.
I can't help putting in my 2 cents...
I have taken to using __get and __set in this manor http://gist.github.com/351387 (similar to the way that doctrine does it), then only ever accessing the properties via the $obj->var in an outside of the class. That way you can override functionality as needed instead of making a huge __get or __set function, or overriding __get and __set in the child classes.

Categories