PHP OOP: do something when object is assigned as a property - php

Consider this hypothetical scenario:
I have a Stomach class, which has a contents property, to which objects of class Food should be assigned. One way to do that would be to use a kind of a setter, e.g. setContents($food).
But lets suppose that we assign food directly, as in $stomach->contents = $orange.
Suppose also that whenever a Food object is assigned to contents we need to change the object's eaten property to true. Is there a way to do that without using a method of an object that it's being assigned to (in this case, the $stomach's setter)?
Basically, my question is: can we call a method whenever an object is assigned as a property to another object? Also, even if it is possible, is it bad design? Sorry if this is a stupid question, all of this is pretty new to me.

The best OOP solution here would be to create a method that indicates an action, like eat().
To ensure that the right Object is eaten, you could define an Interface (Say Food). This interface may define a method setEaten(). The Stomach (though I would prefer Mammal or something similar that actually can eat) can then call setEaten().
Since it created some controversy in the comments, I want to point out that an object definition should as closely as possible reflect what it actually does. To reduce object coupling it is best to avoid directly accessing object properties from an other class, however there is nothing wrong with using setters instead of actions when it makes sense (note that I used one in the definition of Food), and it may often depend on the developer view.
However, here it makes sense. Consider the case "A monkey eats a banana" (for simplicity please allow me to just materialize that banana out of thin air).
$m = new Monkey();
$m->eat(new Banana());
perfect. Now lets try a setter.
$m->setContents(new Banana());
Now we have a problem, the monkey already contains a lot of things, like bones, blood, muscles a brain, etc. So setContents makes no sense here. You could try $m->getStomach()->setContents(new Banana()) but that would just increase object coupling.

I agree with #dualed about the eat method. That said the way to acheive this to make all properties private/protected and then use __get/__set to proxy to the setters.
class Stomach {
protected $contents;
public function setContents(Food $food) {
$this->contents = $food;
$food->eaten = true;
}
public function __set($name, $value) {
$method = array($this, 'set' . $name);
if(is_callable($method)) {
return call_user_func_array($method, array($value));
}
}
public function __get($name) {
$method = array($this, 'get'.$name);
if(is_callable($method)) {
return call_user_func($method);
}
}
}
Youll notic i use is_callable as opposed to method_exists because if youre working on somehting complex with virtual methods is_callable should take those in to account whereas method_exists relies on the method being defined in the class hierarchy.

__set function will help you here. But make sure you dont have any property named contents defined in class. Do it like this
class Stomach{
private $props = array();
public __set($prop, $value){
if($prop === 'contents' and $value instanceof Food){
$this->prop[$prop] = $value;
}
}
}

Related

Calling the variable property directly vs getter/setters - OOP Design

I know this is probably subjective but I read this optimization page from Google for PHP and they suggest use the variable property directly without the need of getters and setters. Understandably I see the performance gain in this but is this really a good design practice to follow?
Their Example using getter/setter:
class dog {
public $name = '';
public function setName($name) {
$this->name = $name;
}
public function getName() {
return $this->name;
}
}
$rover = new dog();
$rover->setName('rover');
echo $rover->getName();
Suggested Optimization:
$rover = new dog();
$rover->name = 'rover';
echo $rover->name;
This would be a welcome change in my design process as I see the need for getters/setters going away, but what other hurdles/benefits might occur in doing this?
This would be a welcome change in my
design process as I see the need for
getters/setters going away, but what
other hurdles/benefits might occur in
doing this?
You lose the ability to implement special get/set logic on a particular property. For properties that are scalars (strings, integers, booleans) maybe this is no problem. But what if you have a property that is a lazy-loaded class instance?
class Document
{
protected $_createdBy;
public function getCreatedBy()
{
if (is_integer($this->_createdBy)) {
$this->_createdBy = UserFactory::loadUserById($this->_createdBy);
}
return $this->_createdBy;
}
}
That trick only works in a method. You could use __get and __set for this logic but as you add properties you end up with a big nasty switch() block:
public function __get($name)
{
switch ($name) {
case 'createdBy':
// blah blah blah
case 'createdDate':
// more stuff
// more case statements until you scream
}
}
If you just want to avoid or put off writing getters and setters, use the __call magic method to trap method calls that follow the getProperty() and setProperty() naming convention. You can put all the default get/set logic in __call and never touch it again:
abstract class Object
{
public function __call($method, $args)
{
$key = '_' . strtolower(substr($method, 3, 1)) . substr($method, 4);
$value = isset($args[0]) ? $args[0] : null;
switch (substr($method, 0, 3)) {
case 'get':
if (property_exists($this, $key)) {
return $this->$key;
}
break;
case 'set':
if (property_exists($this, $key)) {
$this->$key = $value;
return $this;
}
break;
case 'has':
return property_exists($this, $key);
break;
}
throw new Exception('Method "' . $method . '" does not exist and was not trapped in __call()');
}
}
This approach is very fast from a development standpoint because you can just extend the Object class, define some properties, and you're off to the races:
class Foo extends Object
{
protected $_bar = 12345;
}
$foo = new Foo();
echo $foo->getBar(); // outputs '12345'
$foo->setBar(67890); // next call to getBar() returns 67890
$foo->getBaz(); // oops! 'baz' doesn't exist, exception for you
It's slow from an execution standpoint because magic methods are damned slow, but you can mitigate that later by defining explicit getBar() and setBar() methods (because __call is only invoked when you calling a method that isn't defined). But if a particular property doesn't get accessed very often, maybe you don't care how slow it is. The point is, it's easy to add special get/set methods later on and the rest of your code never knows the difference.
I cribbed this approach from Magento and I find it to be very developer-friendly. Throwing an exception when calling the get/set for a property that doesn't exist helps you avoid phantom bugs caused by typos. Keeping property-specific logic in its own get/set methods makes code easier to maintain. But you don't have to write all the accessor methods at the start, you can easily go back and add them without refactoring all your other code.
The question is, what are you trying to optimize? Developer time or code speed? If you want to optimize code speed, make sure you know where your bottlenecks are before building your code around them. Premature optimization is the root of all evil.
This is some kind of micro-optimization. Theoretically, you can later add logic on name get/set by using magic methods (__get and __set) but practically it is not needed so much. And again, practically, this performance improvement only important only if you have everything else so optimized, that even a few microseconds add the value. In this case you can use other optimization techniques like merging all the included PHP files in one, remove type hints, decrease number of function parameters, use plain functions instead of classes. But usually adding a simple caching adds the 10-100x performance boost than all these micro-optimizations.
A boilerplate answer, I'm afraid but I would suggest the following:
If you have no encapsulation problems for your class (enforcing business logic, etc.) by exposing this property to other users, it is perfectly ok to do so.
You could also use the __get and __set magic methods:
class Example
{
private $allowedProps = array('prop1', 'prop2', 'prop3');
private $data = array();
public function __set($propName, $propValue)
{
if (in_array($propName, $this->allowedProps))
{
$this->data[$propName] = $propValue;
}
else
{
// error
}
}
public function __get($propName)
{
if (array_key_exists($propName, $this->data))
{
return $this->data[$propName];
}
else
{
// error
}
}
}
At first I was surprised, I was like ... wtf. But after wrapping my brain on it a few seconds, I realized the example calls the getter function 1 million time in a loop. Of course if the variable is wrapped in a getter, we have added instructions and of course it's going to take longer.
In my opinion in most situations this is very trivial because I have yet to come accross a script that comes event close to calling getters 1 million time when running. If you do need to squeeze performance to the very last drop, it is good to know this optimisation technique.
It depends on whether $name is public or not. If it's not, you can't access/modify it directly. The trade off is exposing your class's internal data elements directly to integrators. This may be OK for some classes, but not for others.
For example, you wouldn't necessarily want others to be able to modify the price of a product directly in a Product class.
I would say this is really a matter of personal preference. If performance is truly that important, then I think you answered your own question.
However, in your first example, you can still access dog::name without the getter/setter just like you do in your second example: $rover->name = 'rover'; because $name is public.
If you specifically want to hide a class member, you would need to declare the variable private or protected and then a getter/setter would be necessary.

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 it reasonable to have a fair amount of public properties in a class?

Or in more specific words, is it "ok" to not be relying on setters and getters?
I'm dealing with a class that checks the availability of rooms and sets public properties of which there are more than a dozen. Things such as:
unitNumber
roomTypes ( array )
codeCorporate
codeGroup
numberKids
numberAdults
numberRooms
currency
minRate
maxRate
soapServer
units ( array )
hotelId
And after an object is instantiated those properties are set with $this-> inside various methods. However the code that deals with the object often sets public properties directly instead of using getter/setter methods:
$object->something = 3;
foreach ($object->things as $thing ) { }
If I have the time to refactor this class..
Should I stick all of these properties in a data array that's a private property, and define __set and __get methods?
Should I make a single getter method for each of the properties?
In my opinion, it is rarely a good idea to have any public members. It increases coupling between classes, and makes refactoring very complicated (should you need it.)
Setters/Getters are the way to go, and the very small performance penalty that you pay for it is usually either optimized away, or trumped by elegance.
To answer your question about array vs. single-getter-per-var, it's a matter of taste. I tend to only keep variables of a similar type within an array, and separate the rest.
I personally have yet to find a truly good reason for a public property, though im open for suggestion :-)
Although i much prefer specified getters/setters for each property (whether that's a proxy to a generalized get($name) or not). I assume you have other code already that uses direct assignment so in that case i would say to proceed with using the magic __get/__set methods.
I think most people will recommend using setters & getters. Right now you're limited to simply setting & fetching the property, but what if you want to log when that property is accessed? Or perhaps you want to run the value by a validation function first (email, phonenumber, zip code, etc). Maybe you'll need to call another function, or set another property. I think you see where I'm heading with this. By using setters & getters, you add a valuable layer of encapsulation to your classes, and 99% of the time this is worth the extra typing you'll need to do ;) Imagine trying to do the examples above without setters & getters. It'd be a big headache to say the least.
Edit: I forgot to mention Doctrine. It's an object relation mapper (ORM) that can automatically setup setters & getters for you (amongst other things). You can check it out at http://www.doctrine-project.org/
I would take a step back and ask some more general questions:
Why am I having to expose this much information; what is using it and why?
Is this class really just a data structure without behavior, in which case should be a private class to some other class?
Does this class serve a single purpose, or is it on the path to becoming monolithic?
You may discover that you are able to create views of an instance of a class to export to a database, display in a form, etc. Check out the "Builder" and "Acyclic Visitor" patterns to start with.
Regarding accessors, I do not see a need to use them for what you are describing: retrieving class properties and internal state information, aka a struct. However, for attributes of a class I could see the benefit in certain cases, but more for retrieving attributes, not for mutations of your object's state.
If I may add my grain of salt several months later :
It is very un-OO to have public properties. Everything should be encapsulated, simply because (among other reasons) using direct attribute manipulation doesn't give you ways to easily refactor or perform (more) control checks when some external source modifies the field. For example, let's say you have a class with many fields that is used throughout a project several times, and that project contains several thousands of files; it's a project that has been running and expanded for a few years now. Let's say that the company is changing it's business model, or that a problem is found with some of the field's data type and now is required to have some validation; will you duplicate that validation in all those thousands of source code that is directly accessing the public member? In PHP, the solution may be simple, but not in most OO programming language (i.g. Java). The fact is that OO is based on encapsulation. In short, encapsulation doesn't only produce clean code, but also highly maintainable (not to say cost-effective and cohesive) code.
Your suggestion of having a private member (array) being manipulated by __get / __set is good. This way, if you need some extra validation along the road, simply create your setter and/or your getter and it will be the end of it. Some may argue with that being counter productive as the code completion cannot kick-in on __get / __set. IMHO, relying on code completion is simply lazy coding. But then again, having every member have it's own getter and/or setter allows you to write a more comprehensive API documentation. Personally, I usually use that technique for internal or very general purpose classes. If all your fields do not require any validation, or there are as you said several dozen of them, then using magic methods would be acceptable, in my opinion.
The bottom line is to avoid direct member access on class instances, period. How you decide to achieve this is strictly up to you. Just make sure that the API is well documented the more abstract you make it.
On a final note, in PHP, if you already have classes that are being used that are not encapsulating their fields, for example something like
class SomeObject {
public $foo;
public $bar;
public $baz;
//...
}
you can simply fix this class without having to refactor anything with something like :
class SomeObject {
private $_foo; // having underscore as prefix helps to know what's private/protected
private $_bar; // inside the code.
private $_baz;
public function __get($name) {
$methodName = 'get'.ucfirst($name);
if (method_exists($this, $methodName)) {
return $this->{$methodName}();
} else {
throw new Exception("Method '{$methodName}' does not exist");
}
}
public function __set($name, $value) {
$methodName = 'set'.ucfirst($name);
if (method_exists($this, $methodName)) {
$this->{$methodName}($value);
} else {
throw new Exception("Method '{$methodName}' does not exist");
}
}
public function getFoo() { return $this->_foo; }
public function setFoo($value) { $this->_foo = $value; }
public function getBar() { return $this->_bar; }
public function setBar($value) { $this->_bar = $value; }
public function getBaz() { return $this->_baz; }
public function setBaz($value) { $this->_baz = $value; }
}
And then
$obj = new SomeObject();
$obj->foo = 'Hello world'; // legacy code support
$obj->setFoo('Hello world'); // same thing, but preferred
And you satisfy both the OO paradigm and having direct access to attributes of an instance. You could also have __call() check for prefix 'get' or 'set' and call __get() and __set() accordingly, but I would not go that far, though this would truly enable general purpose classes to access it's private members via ->member and ->getMember()/->setMember()

When do/should I use __construct(), __get(), __set(), and __call() in PHP?

A similar question discusses __construct, but I left it in my title for people searching who find this one.
Apparently, __get and __set take a parameter that is the variable being gotten or set. However, you have to know the variable name (eg, know that the age of the person is $age instead of $myAge). So I don't see the point if you HAVE to know a variable name, especially if you are working with code that you aren't familiar with (such as a library).
I found some pages that explain __get(), __set(), and __call(), but I still don't get why or when they are useful.
This page will probably be useful. (Note that what you say is incorrect - __set() takes as a parameter both the name of the variable and the value. __get() just takes the name of the variable).
__get() and __set() are useful in library functions where you want to provide generic access to variables. For example in an ActiveRecord class, you might want people to be able to access database fields as object properties. For example, in Kohana PHP framework you might use:
$user = ORM::factory('user', 1);
$email = $user->email_address;
This is accomplished by using __get() and __set().
Something similar can be accomplished when using __call(), i.e. you can detect when someone is calling getProperty() and setProperty() and handle accordingly.
__get(), __set(), and __call() are what PHP calls "magic methods" which is a moniker I think that is a bit silly - I think "hook" is a bit more apt. Anyway, I digress...
The purpose of these is to provide execution cases for when datamembers (properties, or methods) that are not defined on the object are accessed, which can be used for all sorts of "clever" thinks like variable hiding, message forwarding, etc.
There is a cost, however - a call that invokes these is around 10x slower than a call to defined datamembers.
Another useful application of magic methods, especially __get and __set and __toString is templates. You can make your code independent from template engine just by writing simple adapter that uses magic methods. In case you want to move to another template engine, just change these methods only.
class View {
public $templateFile;
protected $properties = array();
public function __set($property, $value) {
$this->properties[$property] = $value;
}
public function __get($property) {
return #$this->properties[$property];
}
public function __toString() {
require_once 'smarty/libs/Smarty.class.php';
$smarty = new Smarty();
$smarty->template_dir = 'view';
$smarty->compile_dir = 'smarty/compile';
$smarty->config_dir = 'smarty/config';
$smarty->cache_dir = 'smarty/cache';
foreach ($this->properties as $property => $value) {
$smarty->assign($property, $value);
}
return $smarty->fetch($this->templateFile);
}
}
Hidden benefit of this approach is that you can nest View objects one inside another:
$index = new View();
$index->templateFile = 'index.tpl';
$topNav = new View();
$topNav->templateFile = 'topNav.tpl';
$index->topNav = $topNav;
And in index.tpl, the nesting looks like that:
<html>
<head></head>
<body>
{$topNav}
Welcome to Foobar Corporation.
</body>
</html>
All nested View objects gets converted to string (HTML to be exact) on the fly, as soon as you echo $index;
Redefining __get and __set can be especially useful in core classes. For example if you didn't want your config to be overwritten accidentally but still wanted to get data from it:
class Example
{
private $config = array('password' => 'pAsSwOrD');
public function __get($name)
{
return $this->config[$name];
}
}
I think it is bad for design you code. If you know and do a good design then you will not need to use the __set() and __get() within your code. Also reading your code is very important and if you are using studio (e.g. Zend studio), with __set() and __get() you can't see your class properties.
PHP allows us to create class variables dynamically which can cause problems. You can use __set and __get methods to restrict this behavior..see the example below...
class Person {
public $name;
public function printProperties(){
print_r(get_object_vars($this));
}
}
$person = new Person();
$person->name = 'Jay'; //This is valid
$person->printProperties();
$person->age = '26'; //This shouldn't work...but it does
$person->printProperties();
to prevent above you can do this..
public function __set($name, $value){
$classVar = get_object_vars($this);
if(in_array($name, $classVar)){
$this->$name = $value;
}
}
Hope this helps...
They're for doing "clever" things.
For example you could use __set() and __get() to talk to a database. Your code would then be: $myObject->foo = "bar"; and this could update a database record behind the scenes. Of course you'd have to be pretty careful with this or your performance could suffer, hence the quotes around "clever" :)
Overloading methods is especially useful when working with PHP objects that contain data that should be easily accessable. __get() is called when accessing a non-existent propery, __set() is called when trying to write a non-existent property and __call() is called when a non-existent method is invoked.
For example, imagine having a class managing your config:
class Config
{
protected $_data = array();
public function __set($key, $val)
{
$this->_data[$key] = $val;
}
public function __get($key)
{
return $this->_data[$key];
}
...etc
}
This makes it a lot easier to read and write to the object, and gives you the change to use custom functionality when reading or writing to object.
Example:
$config = new Config();
$config->foo = 'bar';
echo $config->foo; // returns 'bar'
One good reason to use them would be in terms of a registry system (I think Zend Framework implements this as a Registry or Config class iirc), so you can do things like
$conf = new Config();
$conf->parent->child->grandchild = 'foo';
Each of those properties is an automatically generated Config object, something akin to:
function __get($key) {
return new Config($key);
}
Obviously if $conf->parent already existed, the __get() method wouldn't be called, so to use this to generate new variables is a nice trick.
Bear in mind this code I've just quoted isn't functionality, I just wrote it quickly for the sake of example.
Probably not the cleanest design in the world but I had a situation where I had a lot of code that was referencing an instance variable in a class, i.e.:
$obj->value = 'blah';
echo $obj->value;
but then later, I wanted to do something special when "value" was set under certain circumstances so I renamed the value variable and implemented __set() and __get() with the changes I needed.
The rest of the code didn't know the difference.

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