<?php
class Item {
public function __set($name, $value){
throw new Exception('Overloading is forbidden.');
}
public function __get($name){
throw new Exception('Overloading is forbidden.');
}
}
I want to make sure I set all required object properties, but if I mistype a property name PHP just adds a new property and the prop I wanted to initialize remains null.
From your comment:
I want to make sure I set all required object properties, but if I mistype a property name PHP just adds a new property and the prop I wanted to initialize remains null.
If you want to make sure you have set all required object properties, consider making them required arguments to the constructor, e.g.
class Item
{
public function __construct($required, $alsoRequired, $moreRequired)
{
// assign to corresponding properties
}
}
This way you can be sure the object is in valid state.
If you cannot set them at object creation, consider adding a validator method to your object. Call this method before doing any critical things with your object to make sure all required properties are set. This would be a much more useful addition to your API than implementing a typo safeguard.
In addition, you should not have an issue with this if you use proper setters. Access your object through an interface instead of assigning properties directly. If you mistype a method name, PHP will tell you (unless you implemented __call).
On a sidenote, __get and __set are not lazy replacements for proper Getters and Setters. They are interceptors that will be triggered when trying to access a non accessible property. This is much more related to error handling. Also note that they are much slower than proper Getter and Setter.
Just using the constructor will not solve the problem.
The problem he describes is very common and throwing an exception if __get or __set are called is a nice idea.
If you want to, you can make those final but I see no reason why you should do this. Only classes that require dynamic propertys will override the methods.
The only improvement I can suggest is enclosing the method definitions in
//DEBUG
and
///
When you are finished debugging you can then easily run a replace(//*DEBUG with /*DEBUG) on all your code files and comment out __get and __set
Using http://php.net/manual/en/function.get-class-vars.php you can check for existence of the variable and for example throw exception when fails
Related
I have two simple questions regarding OOP practice of classes. I expect that the answers will lean toward subjective preferences.
Let's say we have a class like a blog POST, which has private variables such as id, author, etc. I see in Symfony demo project the getter and setter methods are declared for almost each variable specifically. If the list is long, this seems quite tedious. So what about creating a method like the following?
function setProp($variable, $val){
if($variable !== 'id'){
$this->$$variable = $val;
}
}
I see that the Symfony demo created three entity classes: Comment, Post, User. The addComment and removeComment methods are in Post class and takes a typecast argument of Comment class. Can we make a removeComment method in the Comment class itself? Because I feel that most of the time the comment id would be the first piece of info passed to the script, so why not have such a method handy in the class itself? And is it 'bad' to have repeated methods that perform the same job in different classes?
The purpose of accessor methods is to provide a way to hook into the action of setting or getting a property.
For basic operations, you don't need accessor methods. They may seem pointless just assigning or returning a value.
One of the main purposes of accessor methods is restricting access to a property. You may want, under some conditions, to prevent the changing of a property. If the property is public, there is nothing you can do to prevent writing to the property. But if your property is private and you have a setter, then you may just not set the property under certain conditions.
private $allowed = true;
private $data;
public function getData() {
return $this->data;
}
public function setData($data) {
if ($this->allowed) {
$this->data = $data;
}
}
public function block() {
$this->allowed = false;
}
In this example, you may block write-access to the data property by setting the allowed property to false.
There are many other cases that make use of accessors (storing data outside the object, converting values, etc.)
It is good practice to use setters and getters from the start because if you create them later, you have to replace all references to the property with references to the setter/getter.
you can look at magique method __set (http://php.net/manual/fr/language.oop5.overloading.php#object.set) but it's better to write each setXxxx
I have 2 classes: User and Router
In my script, class User is instantiated first to set user data, then class Router is instantiated to set page data.
$user = new User();
$router = new Router();
Inside one of Router's methods, I need to invoke $user->getSuperPrivileges(). This function queries the DB and sets extra parameters in the $user object, then returns them.
I could pass $user as a parameter of Router($user) and save it as a property in the construct function, but I believe this would only create a clone of the real object. Problem with this is that the values set by $this->user->getSuperPrivileges() would only be accessible by the clone, and not in the global script by the real object. In other words, I would need to invoke the getSuperPrivileges() method once again in the real object to set these properties again, which is counterproductive.
What is the best way to achieve what I want (access the real object $user and its methods from inside $router, without having to create a clone passed as a function parameter)?
As pointed out below by #hek2mgl, in php5 every object variable is a reference. The __construct magic method would not work at all prior to that anyway so we can assume that OPs example should work regardless.
http://3v4l.org/6dKL0
The following lines are really pointless given the above example.
have you tried passing the $user object as a reference?
class Router{
function __contruct(&$user){
$this->user=$user;
}
}
new Router($user);
in that case how about a singleton?
function user(&$userO){
static $user;
if(!is_array($user)) $user=array();
if(is_object($userO)) $user[$userO->uid]=$userO;
if(is_string($userO)) return $user[$userO];
}
class Router{
function __construct($user){
$this->uid=$user->uid;
}
function __get($k){if($k=='user') return user($this->uid);}
}
To explain a little more, the user function stored the user objects, keyed by a unique identifier in a static array, the __get magic method allows you to intercept calls to the user property on the router object and return the statically saved object from the singleton function.
You can create the $user object and inject it into $router object using constructor injection. But what you are doing should be just fine. You should be able to use that object for whatever you need within your router class. Especially if the database maintains the privilege state.
If you must use only one instance of the class check out the section on Singleton patterns at: http://www.phptherightway.com/pages/Design-Patterns.html and you can get an idea of how to achieve this.
I'd try and apply the Dependency Injection pattern. The point is that methods should be passed all they need to operate.
Meaning the method in your router which operates on a user should be passed said user.
class Router {
method privilegiateUser(User $user) {
// notice the typehint
// php will enforce that your method receives a User
$user->getSuperPrivileges();
}
}
I'd disapprove passing the User to your Router's __construct() if it's to be used only once and not with each script run. Think about it that way:
Is a User a property of a Router in the same way than a Name is a property of a User?
I have done a few projects lately using a Database Object super class which I use for quick one off record query/update, and extending with appropriate classes, such as a User class.
I found that many classes I was writing had the exact same methods: query_values(), update(), delete(), etc.
So I came up with a class with a constructor that looks like this:
public function __construct($table, $db_object, $record_id = null){
$this->db = $db_object; // Database object with query methods
$this->table = $table; // The name of the database table
$this->get_column_data();
if(!is_null($record_id)){
// This retrieves all column values,
// stores into private $fields array property
$this->query_values($record_id);
}
}
And a child class constructor looks like this:
public function __construct($db_object, $record_id = null){
parent::__construct($this->table, $db_object, $record_id);
}
Where the $table property is defined at the top, as we should know which table this specific object works with.
Now, all common record management methods are in one place, and methods specific to the class are all that is defined in their respective child-classes.
The biggest drawback I see here is that all data fields are pulled and are encapsulated within the $fields property, so either generic get and set methods need to be defined (which I typically do) which almost negates the encapsulation*, or a method must be defined specifically for each property we want to expose.
*Example:
$user_id = $User->id; // NOT USING MY METHOD
vs.
$user_id = $User->_get('id'); // ACCESSES $User->fields['id']
Do you see this as a drawback, or a plus? The goal being ease of use, object-orientation (encapsulation), and just being plain awesome!
Well, you could make your life easy and use PHP's magic overloading __call method to create generic getters and setters. You could add the following method to your "Database Object super-class":
/**
* Create magic getter and setter methods to access private $fields array
*/
public function __call($method, $args)
{
$prefix = substr($method, 0, 3);
$prop = lcfirst(substr($method, 3));
if (isset($this->fields[$prop])) {
if ($prefix == 'get') {
return $this->fields[$prop];
} elseif ($prefix == 'set') {
if ( ! isset($args[0])) {
$msg = 'Missing argument: ' . get_class($this) . "::$method must specify a value";
throw new InvalidArgumentException($msg);
}
$this->fields[$prop] = $args[0];
return;
}
}
$msg = 'Invalid method: ' . get_class($this) . "::$method does not exist";
throw new BadMethodCallException($msg);
}
So let me explain what's going on here. The magic __call method will receive calls to any object method that does not match one of the object's concrete methods. It receives as parameters the name of the method that was called and an array of its arguments.
The __call method above does a quick substr check to see if the method was a "getter" or "setter" (using the first three letters of the method name). It expects that your $fields array stores lower-case "property" names (lcfirst) and uses everything after the setter/getter prefix as the expected property name.
If a property matches the getter method then that property it is returned. If not, the SplException BadMethodCallException is thrown. This is a best practice since the inclusion of the Spl exceptions in PHP.
Likewise a setter method will also throw the SplException InvalidArgumentException if no argument was specified.
PHP's magic methods will change your life. You can also use __get and __set to assign $fields array values in a similar fashion without making faux method calls. Get excited :)
You can have the best of both worlds by implementing a few magic methods on your objects. For example, by implementing __get, __set and __isset you can make the property accesses look "natural" (e.g. $user->id) while at the same time not having to define properties in each separate subclass.
For example, the implementation of __get could check at runtime the $fields member to see if the property you are attempting to get is valid for the specific object class. You can write this generic implementation once in the base class, and just populate $fields accordingly to make it work with any kind of object. This is actually what all modern mapping tools do, including probably all of the PHP frameworks that you have heard of.
As an aside, I hope you are caching the table schema per-class inside get_column_data. It would be really inefficient to requery the database for the schema of the same table every time you construct an object of the corresponding class.
There are two major approaches:
PHP file creation
All big ORM / database projects I know do not let the user write all getter methods himself. Both Doctrine and Propel use automatical generators to create real PHP files with the getter methods when you call a command line script. These files contain so called Base methods which are automatically extended by some classes you will put your own code in (so recreation of the base class will not delete your own code).
Advantage of file creation is that you have a well defined interface (some people dislike it when callable methods are not visible directly within the source code).
Magic methods / overloading
A database engine I have seen at client’s application used magic methods to simulate getters and setters. You could either use magic methods __get() and __set() or __call() (called “overloading” in PHP documentation) for this. Which method to use depends on how you want to access your values (most common $obj->property or $obj->getProperty()).
Advantage of overloading is that you do not need to write a complex PHP code generator, nor do you need to call a command line script each time you change your database design.
In my quest in trying to learn more about OOP in PHP. I have come across the constructor function a good few times and simply can't ignore it anymore. In my understanding, the constructor is called upon the moment I create an object, is this correct?
But why would I need to create this constructor if I can use "normal" functions or methods as their called?
cheers,
Keith
The constructor allows you to ensure that the object is put in a particular state before you attempt to use it. For example, if your object has certain properties that are required for it to be used, you could initialize them in the constructor. Also, constructors allow a efficient way to initialize objects.
Yes the constructor is called when the object is created.
A small example of the usefulness of a constructor is this
class Bar
{
// The variable we will be using within our class
var $val;
// This function is called when someone does $foo = new Bar();
// But this constructor has also an $var within its definition,
// So you have to do $foo = new Bar("some data")
function __construct($var)
{
// Assign's the $var from the constructor to the $val variable
// we defined above
$this->val = $var
}
}
$foo = new Bar("baz");
echo $foo->val // baz
// You can also do this to see everything defined within the class
print_r($foo);
UPDATE:
A question also asked why this should be used, a real life example is a database class, where you call the object with the username and password and table to connect to, which the constructor would connect to. Then you have the functions to do all the work within that database.
The idea of constructor is to prepare initial bunch of data for the object, so it can behave expectedly.
Just call a method is not a deal, because you can forget to do that, and this cannot be specified as "required before work" in syntax - so you'll get "broken" object.
Constructors are good for a variety of things. They initialize variables in your class. Say you are creating a BankAccount class. $b = new BankAccount(60); has a constructor that gives the bank account an initial value. They set variables within the class basically or they can also initialize other classes (inheritance).
The constructor is for initialisation done when an object is created.
You would not want to call an arbitrary method on a newly created object because this goes against the idea of encapsulation, and would require code using this object to have inherent knowledge of its inner workings (and requires more effort).
In my quest in trying to learn more about OOP in PHP. I have come across the constructor function a good few times and simply can't ignore it anymore. In my understanding, the constructor is called upon the moment I create an object, is this correct?
But why would I need to create this constructor if I can use "normal" functions or methods as their called?
cheers,
Keith
The constructor allows you to ensure that the object is put in a particular state before you attempt to use it. For example, if your object has certain properties that are required for it to be used, you could initialize them in the constructor. Also, constructors allow a efficient way to initialize objects.
Yes the constructor is called when the object is created.
A small example of the usefulness of a constructor is this
class Bar
{
// The variable we will be using within our class
var $val;
// This function is called when someone does $foo = new Bar();
// But this constructor has also an $var within its definition,
// So you have to do $foo = new Bar("some data")
function __construct($var)
{
// Assign's the $var from the constructor to the $val variable
// we defined above
$this->val = $var
}
}
$foo = new Bar("baz");
echo $foo->val // baz
// You can also do this to see everything defined within the class
print_r($foo);
UPDATE:
A question also asked why this should be used, a real life example is a database class, where you call the object with the username and password and table to connect to, which the constructor would connect to. Then you have the functions to do all the work within that database.
The idea of constructor is to prepare initial bunch of data for the object, so it can behave expectedly.
Just call a method is not a deal, because you can forget to do that, and this cannot be specified as "required before work" in syntax - so you'll get "broken" object.
Constructors are good for a variety of things. They initialize variables in your class. Say you are creating a BankAccount class. $b = new BankAccount(60); has a constructor that gives the bank account an initial value. They set variables within the class basically or they can also initialize other classes (inheritance).
The constructor is for initialisation done when an object is created.
You would not want to call an arbitrary method on a newly created object because this goes against the idea of encapsulation, and would require code using this object to have inherent knowledge of its inner workings (and requires more effort).