why use __set() & __get() method rather than declare a property as public - php

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.

Related

Regulating write access to object properties based on context

class SomeObject {
protected $foo,
$bar;
protected $context;
public function __construct($context) {
$this->context = $context;
}
public function setFoo($val) {
if ($this->context == 'public') {
throw new \Exception('It is impossible to modify foo property in public context!');
}
$this->foo = $val;
}
public function setBar($val) {
if ($this->context == 'api') {
throw new \Exception('It is impossible to modify bar property in API context!');
}
$this->bar = $val;
}
}
As you can see from this piece of "code" - object restricts setters depending on context value. This code is really hard to maintain. How can we rewrite it to make it beautiful and easy maintainable?
My thoughts are:
Make $context an object(s) implementing interface
isAllowed($object, $propertyName).
After making $context an object we have to thing about how can we store "restrictions" in $context object taking in mind there are a lot of different objects similar to SomeObject.
In every setter I should check $this->context->isAllowed($this, 'foo') - it looks not good. So, probably we want to add some "proxy" over SomeObject?
Passing $context to constructor also seems rather ugly for me.
What's your ideas about it?
Just two general observations:
You may want to segregate your classes into two parts: an immutable base class and a mutable extension:
class Foo {
protected $bar, $baz;
}
class MutableFoo extends Foo {
public function setBar($bar) {
$this->bar = $bar;
}
..
}
This easily solves the problem when the context is defined at object instantiation time and won't ever change. Instead of instantiating with a different context which determines the mutability, you simply instantiate a mutable or immutable version of the class.
If you still need more runtime checks, maybe simply using assertions is the best way to simplify the code:
public function setBar($bar) {
$this->assertCanSet('bar');
$this->bar = $bar;
}
protected function assertCanSet($property) {
if (!/* can set $property */) {
throw new Exception("Cannot set property $property");
}
}
Maybe on the construct, fill a list of restricted methods.
so, for instance :
class SomeObject {
protected $foo,
$bar;
protected $context;
protected $restrictedMethods;
public function __construct($context) {
$this->context = $context;
if($this->context == 'public') {
$this->restrictedMethods['setFoo'] = true;
} else if ($this->context == 'api') {
$this->restrictedMethods['setBar'] = true;
}
}
public function setFoo($val) {
if ($this->isRestricted('setFoo')) {
throw new \Exception('It is impossible to modify foo property in '.$this->context.' context!');
}
$this->foo = $val;
}
public function setBar($val) {
if ($this->isRestricted('setFoo')) {
throw new \Exception('It is impossible to modify bar property in '.$this->context.' context!');
}
$this->bar = $val;
}
protected function isRestricted($methodName) {
return array_key_exists($methodName, $this->restrictedMethods);
}
}
If you are trying to write good OOP, then "Interface Segregation" from the SOLID principle may be useful to you.
interface IBase
{
public function doMethod1();
public function doMethod2();
public function doMethod3();
}
interface IFoo extends IBase
{
public function setFoo($val);
}
interface IBar extends IBase
{
public function setBar($val);
}
function doWork(IBase $obj, $val)
{
$obj->doMethod1();
$obj->doMethod2();
$obj->doMethod3();
if ($obj instanceof IFoo) {
$obj->setFoo($val);
}
if ($obj instanceof IBar) {
$obj->setBar($val);
}
}
I doubt this example is exactly what you need, but I will use it to explain the basic idea.
A class should only have a "Single Responsibility". What that responsibility encompasses can vary however, so in general it is best to limit a class's functionality to a single area of concern as best you can.
If you want to follow "Liskov substitution", then throwing exceptions like that in your functions simply because the "context" was irrelevant, violates this principle.
Enter "Interface segregation":
By implementing an interface, you are (to a certain extent) guaranteeing to the caller of the implemented methods, that those methods will work. By excluding them, you are telling the caller that those methods don't exist.
In the example, the doWork function expects an instance of IBase, and safely calls the methods of that interface. After that, it runs introspection of the object to determine if other "applicable" methods are available.
The goal behind interface segregation is to limit the amount of unneeded features a class is forced to implement, so for you, if the context is public, it shouldn't need the setFoo method.
A clean solution would be to have an ObjectFactory class that creates different objects based on a $context parameter, and having two separate classes (with a common base class) that allows writing to the appropriate properties.
Please find below a possible implementation for your schema:
/**
* Base class that allows subclasses to define which properties are
* writable via setters. Subclasses must not add public setters,
* otherwise the mechanism within this class will not work; subclasses
* can add protected setters, though
*/
class PropertyRestricter {
// only properties listed here are writable
// to be initialised by subclasses
protected $writableProperties;
public function __construct() {
// default implementation allows no writable properties
$this->writableProperties = array();
}
public function __call($method, $arguments) {
$matches = false;
// check if a setter was called, extract the property name
// there needs to be at least one argument to the setter
if(count($arguments) && preg_match('/^set([A-Z][a-zA-Z0-9_]+)$/',$matches)) {
$propName = $matches[1];
$propName[0] = strtolower($propName[0]);
// update the property with the given value
// or throw an exception if the property is not writable
if(is_array($this->writableProperties) && in_array($propName, $this->writableProperties)) {
$this->{$propName} = $arguments[0];
} else {
throw new Exception(get_class() . ": $propName is not writable");
}
} else {
// not a setter, nor a public method
// maybe display a user error
}
}
}
/**
* Common properties for the actual classes
*/
class BaseObject extends PropertyRestricter {
protected $foo, $bar;
}
class PublicObject extends BaseObject {
public function __construct() {
parent::__construct();
$this->writableProperties = array('foo');
}
}
class APIObject extends BaseObject {
public function __construct() {
parent::__construct();
$this->writableProperties = array('bar');
}
}
class ObjectFactory {
public function createObject($context) {
switch($context) {
case 'public': return new PublicObject();
case 'api': return new APIObject();
default: return null;
}
}
}
The root of the objects is the PropertyRestricter class that allows subclasses to define which properties are writable. It makes use of the magic method __call() in order to be able to intercept setter calls and to validate the attempt to write to the property. However please note that this works only if subclasses don't add public setters for their properties.
The next level is the BaseObject class, which only defines the two properties, in order to reduce code redundancy.
The last level contains the two classes that get instantiated by the ObjectFactory: PublicObject, 'APIObject. These classes simply initialise thewritablePropertiesarray, as the rest of the work is done by thePropertyRestricter` class.
This is also a scalable solution, as it allows adding as many properties and subclasses as needed, each subclass defining its property writing rules.
Also the property update within the __call() method can be customised, I implemented it in the simplest way by directly setting the property. Actual setters can be used in subclasses and __call() can be updated to call the setters, with the mention that the setters need to be protected in order for the mechanism to work.

How to set property type for property created within constructor - PHP

I am using PHP.
As properties defined within the core of a class must be static, I have to create one property in the constructor to allow me to create it with a dynamic name.
Example
final class User extends Z
{
}
Abstract Class Z
{
function __constructor()
{
$x = get_called_class() . 'Id';
$this->$x = null;
}
}
If we var_dump() obj User we would have
object(User)#1 (1) {
["UserId"]=>
NULL
}
I want to set property A as a protected type.
How can I do this?
Or is there a better way to set the name of my dynamically named property?
The best you can probably do is use magic getter/setter methods and an array of properties:
<?php
abstract class Z
{
private $properties = array();
function __construct()
{
$x = get_called_class();
$this->$x = null;
}
public function __set($prop, $val)
{
$this->properties[$prop] = $val;
}
public function __get($prop)
{
return $this->properties[$prop];
}
}
class A extends Z
{
}
var_dump(new A());
Note that the property is private, child classes will have to go through the __get and __set methods.
You could also restrict dynamic properties to only be the called class name.
<?php
abstract class Z
{
private $classprop;
function __construct()
{
$x = get_called_class();
$this->$x = true;
}
public function __set($prop, $val)
{
if ($prop !== get_called_class()) {
throw new \UnexpectedValueException('Cannot Set Dynamic properties Other than Class Name');
}
$this->classprop = $val;
}
public function __get($prop)
{
if ($prop !== get_called_class()) {
throw new \UnexpectedValueException('Cannot Get Dynamic properties Other than Class Name');
}
return $this->classprop;
}
}
class A extends Z
{
}
I would encourage you to think pretty carefully about why you need dynamic properties like this.
The easiest answer would be don't - just use some generic base property, like protected $Id; and save the trouble.
If you really must have "dynamic" properties in class extensions, probably the simplest would be to declare the property in the extending class:
final class User extends Z {
protected $UserId;
}
Then your code in Z will pick out this protected property. It's psudo-dynamic, since the property name is determined by the coder, and the script only "finds" it when the constructor is run, but if your property name is already determined by something fixed at compile-time, such as the class name, there's functionally no difference.
For truly dynamic property creation, you'd have to add in a runkit, an extension that allows you to programmatically change classes, methods, properties, and functions from within the script itself. A good one seems to be this one by Demitry Zenovich. Unless you have some truly complex functions to carry out to justify the time working with it, though, it ain't going to make your life easier.

Do all properties need to be protected?

Hello I was wondering why some popular PHP libraries make all properties protected, then add get and set methods for them like:
protected
$a = false;
public function getA(){
return $this->a;
}
public function setA($value){
$this->a = (bool)$value;
}
What's the benefit of this and why not simply make the property public?
OOP real world scenario:
Imagine you have a class Vehicles and they have (protected) wheels. You have different Vehicles with wheels, but to addWheel to a Bike, is different from addWheel to an Aircraft.
Code advantage:
Using getter and setter, you can use typehinting.
Compare those snippets:
class Car {
public $wheels;
}
$bmw = new Car;
$bmw->wheels = 'piece of paper';
The above code let's you add anything as a wheel, but can you use a piece of paper as a wheel?
Now with getter and setter:
class Car {
protected wheels;
public function __construct() {
$this->wheels = new ArrayIterator;
}
public function addWheel(Wheel $wheel) {
$this->wheels->add($wheel);
return $this;
}
public function removeWheel(Wheel $wheel) {
$this->wheels->remove($wheel);
return $this;
}
}
class Wheel {
}
$bmw = new Car;
$bmw->addWheel('piece of paper'); // <-- throws an error!
// paper cannot be used as a wheel
$bmw->addWheel(new Wheel); // <-- good :-)
More code, to be more straightforward. Imagine you have RearWheels and FrontWheels:
class Wheel {
}
class FrontWheel extends Wheel {
}
class RearWheel extends Wheel {
}
class Car {
protected wheels;
public function __construct() {
$this->wheels = new ArrayIterator;
}
public function addWheel(Wheel $wheel) {
// check for already existing Wheels here.
// Pseudo Code:
if (wheels typeof RearWheel > 2) {
throw new Exception('cannot add more than 2 RearWheels to the Car');
}
return $this;
}
public function removeWheel(Wheel $wheel) {
$this->wheels->remove($wheel);
return $this;
}
}
It allows you to add custom setters and getters in case you want to add extra functionality, and also is sometimes used to track the "dirty" state (if an object has been changed since it was loaded from the DB).
It´s also due to the fact that PHP does not have a "native" syntax for read only properties.
Addition:
I didn´t quite get my point across in my original answer that its in many cases due to how PHP works.
Consider this PHP example where we use metaprogramming to dynamically call custom setters.
class Foo {
protected $bar;
// use some metaprogramming magic to automatically call setter
public function __set($key, $val){
$setter = "set".ucfirst($key);
if (method_exists($this, $setter)){
$this->$setter($val);
}
else {
throw new Exception("$key value cannot be set, looking for $setter in ".get_class($this));
}
}
// enforce the Type of bar
public function setBar(String $val){
$this->bar = $val;
}
}
$foo = new Foo();
// this should fail
$foo->bar = 12;
// Catchable fatal error: Argument 1 passed to Foo::setBar() must be an instance of String, integer given
What I´m trying to get at here is that you are not a Bad Programmer for having public properties, but rather that most PHP frameworks dodge around the PHP object model that requires properties to be protected for inheritance and reflection to work in many cases.
Popular libraries which support their classes to be extended or inherited make their fields protected. This is concept of OOP that there must be abstraction and encapsulation in terms of data. Everyone shall not be allowed to directly access the data. Only member of a class shall access its data. This is reason fields are marked protected so that only inherited class's objects or same class's objects shall have access to data. All others must use get and set methods for reaching data. Keeping the code managed, clean and safe.

Call __get() on protected properties with -> operator?

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.

Making a global variable accessible for every function inside a class

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'.

Categories