This is not so much a question about execution as it is a question about improving code. I am a 2nd year student, we started to touch on OOP recently and I am finally getting the hold of it....sort of.
I realize this is a very basic question, but what better place to learn from some of the best.
My Question
I have a class which creates a new match. My problem is that I am sure the code is unnecessary long and can get much improved (just keep in mind it is beginner level).Specifically I would like to know:
Can I change the below into 1 setter and 1 getter method?
I would like to use the rand() function for match ID can I do this inside the setter function of setMatchId or should it be done outside of the class?
Thank you very much for taking the time to read this.
<?php
class match{
private $matchId;
private $team1;
private $team2;
private $venue;
function __construct($pMatchId, $pTeam1, $pTeam2, $pVenue){
$this->matchId = $pMatchId;
$this->team1 = $pTeam1;
$this->team2 = $pTeam2;
$this->venue = $pVenue;
}
function setMatchId($pMatchId){
$this->matchId = $pMatchId;
}
function getMatchId(){
return $this->matchId;
}
function setTeam1($pTeam1){
$this->team1 = $pTeam1;
}
function getTeam1(){
return $this->team1;
}
function setTeam2($pTeam2){
$this->team2 = $pTeam2;
}
function getTeam2(){
return $this->team2;
}
function setVenue($pVenue){
$this->venue = $pVenue;
}
function getVenue(){
return $this->venue;
}
} // c;lass match
$x = new match("1", "Patriots", "Chargers", "Newlands");
echo $x->getMatchId();
echo'<br />';
echo $x->getTeam1();
echo'<br />';
echo $x->getTeam2();
echo'<br />';
echo $x->getVenue();
?>
How often are teams or venues going to change for a match? I think you should get rid of the setters since you're already providing all the necessary data through your constructor.
You can indeed change your code to work with a single getter and setter methods, but I'd strongly discourage that. IDE's won't be able to assist you with code completion if you implement such methods but, most importantly, you should never blindly implement getters and setters in your entities if they have no reason to exist.
Let the design guide you on that. Start by passing everything your objects need through their constructors and only add getters/setters when you need them, not the other way around.
In terms of the randomness of the ID, you could use UUIDs for them. You could use this library to create them. I'd pass them through its constructor as well.
You can use __set and __get magic methods of PHP.
private $data = array(); // define property array
public function __set($name, $value) // set key and value in data property
{
echo "Setting '$name' to '$value'\n";
$this->data[$name] = $value;
}
public function __get($name) // get propery value
{
if(isset($this->data[$name])) {
return $this->data[$name];
}
}
You can write your existing code as below:-
class Match{
private $data = [];
function __construct($property=[]){
if(!empty($property)){
foreach($property as $key=>$value){
$this->__set($key,$value);
}
}
}
public function __set($name, $value) // set key and value in data property
{
// echo "Setting '$name' to '$value'\n";
$this->data[$name] = $value;
}
public function __get($name) // get propery value
{
if(isset($this->data[$name])) {
return $this->data[$name];
}
}
}
Set properties using construct method
$x = new match(["matchId"=>"1", "team1"=>"Patriots","team2"=>"Chargers","venue"=>"Newlands"]);
echo '<pre>'; print_r($x);
Set properties without construct method
$x = new match;
$x->matchId = '1'; // 1
$x->team1 = 'team1'; // Patriots
$x->team2 = 'Chargers'; // Chargers
$x->venue = 'Newlands'; // Newlands
echo '<pre>'; print_r($x);
output:-
Match Object
(
[data:Match:private] => Array
(
[matchId] => 1
[team1] => Patriots
[team2] => Chargers
[venue] => Newlands
)
)
Now you can access and set propery by below way:-
// Get all properties values
echo $x->matchId; // 1
echo $x->team1; // Patriots
echo $x->team2; // Chargers
echo $x->venue; // Newlands
// Overwrite existing values
$x->team1 = 'new team1';
// Get updated value
echo $x->team1; // new team1
Hope it will help you :)
The first question:
Can I change the below into 1 setter and 1 getter method?
[EDIT] Reply to first comment:
You can, but you shouldn't.. To me it's better keep all setters and getters parted. You might want to get only a specific field when using your match object instance in your code. So if you need to get team1 or team2 it's better to have two different getter methods.
The second question:
I would like to use the rand() function for match ID can I do this inside the setter function of setMatchId or should it be done outside of the class?
Well, in my opinion, the best way of handle it is to disallow any access to the $matchId field making it private and removing any setter method.
Then, you should place the rand generation inside the constructor or, if you want to keep it parted in a specific function you could make a public getter like this:
public getMatchId(){
if ($this->matchId != null)
return $this->matchId
// Generate it with rand()
$this->matchId = rand()
return $this->matchId;
}
In the constructor then simply call the getMatchId() method.
By the way, this solution doesn't help you with getting a unique match identifier, to achieve that you should generate it not purely randomly but using something that is dependant of the informations of the Match (for instance you could use a combination of team1, team2 and venue) and/or keep track of used matchid (a static field or a database could be helpful)
[EDIT] Reply to second comment:
I'm using the if statement in the getter because this getter is thought to generate the $matchId when it's called for the first time, while it'll always return the previously generated $matchId for the other calls.
You question made me think of another possible implementation. If you want to avoid the if then you should generate the $matchId in the constructor.
This way should be fine:
public __construct($team1, $team2, $venue){
$this->matchId = rand();
$this->team1 = $team1;
$this->team2 = $team2;
$this->venue = $venue
}
public getMatchId(){
return $this->matchId;
}
There are multiple answers covering how to do setters and getters in various degrees of complexity and magic. In this post I would rather focus on the design quality of your class Match. This is based on the design idea related to what do you want to use your class for?
Some typical statements answering this question:
Keep record of a given match – In other words it needs to hold information related to one match, i.e. venue, homeTeam, awayTeam, result?, and possibly a matchId related to storing the result somewhere
Set the result of a match – You'll create the match, and then a little later you'll set the actual result of the match.
Store a match – If you don't store it anywhere it is kind of futile to keep track of the match, so most likely you would need some interface either to a database, or some mean to get all information related to a match ready for storing into a file or similar
Ability to retrieve the details of a match – If not getting all information at the same time, you could opt for a getter for the specific values you'll want.
For me I don't see the need for changing the team or venue, as that would mean a new match in my world. And I would definitively not implement a generic setter which would allow for setting whatever to whatever. A generic setter is a security risk in my world.
Alternate implementation
Adhering to the statements given I would write something similar to this:
<?php
class Match {
private $matchId;
private $homeTeam;
private $awayTeam;
private $venue;
private $result;
function __construct($venue, $homeTeam, $awayTeam, $matchId = NULL) {
$this->venue = $venue;
$this->homeTeam = $homeTeam;
$this->awayTeam = $awayTeam;
if (is_null($matchId)) {
$this->matchId = uniqid();
} else {
$this->matchId = $matchId;
}
// In PHP7: $this->matchId = $matchId ?: uniqid();
$this->result = "";
}
function setResult($result){
$this->result = $result;
}
function getAll(){
return array($this->venue, $this->homeTeam, $this->awayTeam,
$this->matchId, $this->result);
}
function __get($name) {
if (property_exist($this, $name)) {
return $this->$name;
}
}
function __set($name, $value) {
if (property_exist($this, $name)) {
$this->$name = $value;
}
}
?>
Some comments related to this code:
homeTeam and awayTeam – Having variables name team1 or team2 is a code smell, to me. I would either create an array for those, or find better names. In this case I opted for better names to make a clear distinction between the two variables.
__construct() – When creating a match the default value for matchId indicates that it will be set to a uniqid(). I consider this a better practice rather then using a random value. And it still allows for setting a specific match id if you want to provide this.
Based on the assumption that you don't know the result when the match is created, the result is set to an empty string for starters.
setResult() – As this is the only part of a match I foresee changing I provided a setter for this value.
getAll() – This returns an array of all the values, ready for storing somewhere. If you like this could easily be changed into a comma separated string or whatever format you would like for post-processing. It could even be a dictionary, but I just used a simple array to keep it simple.
__get() and __set() – Contrary to some of the other answers this getter (and setter) is a little safer to use as it verifies that the actual property is defined in this class using property_exist().
I'm not sure if I would actually have the generic setter, but if you'd like one, this is a better option as it doesn't allow for creation of new properties to your class at runtime.
Usage of class
Here is some simple usage of the class (if my untested code actually works, that is):
<!php
$m = new Match("Newlands", "Patriots", "Chargers");
// Time passes
$m->setResult("102-32");
echo 'In the game ' . $m->homeTeam . ' vs ' . $m->awayTeam
echo ' at ' . $m ->venue ' the result was ' . $m->result . ' <br />'
// Append the match to a file
$fp = fopen('allmatches.csv', 'a');
fputcsv($fp, $m->getAll());
fclose($fp);
?>
This uses fputcsv to format the array into a line in the csv format. Having a method or some way to create a match from an array is left as an exercise. You possibly have a static method taking a file name as a parameter, and return an array of matches.
There is no good or bad model when you aren't trying to solve a well-defined problem just like there's no good answer to a bad question.
Before even worrying about things such as getters and setters you need to determine the purpose of the model and what problem it is trying to solve.
I understand that this is probably just a modeling exercise, but if you want it to have any value, start by defining your problem domain and then work out the solution.
For instance, if you are modeling an application service that allows to query a list of matches, then perhaps Match is a simple immutable data structure that acts as a Data Transfer Object.
If you were modeling a ViewModel that is meant to be 2-way bound to a CRUD screen allowing to update the details of a Match then perhaps you'd have a data container with public getters and setters like you had.
If you were crafting a tournament system domain model and had a use case such as: "Tournament administrators will enter the scoring of a match after it's completion. The outcome will be automatically resolved by the system. The possible results are that the home/away team wins or a draw."
Then perhaps Match would carry a behavior such as (pseudo-code):
scoring = new Scoring(homeTeamScore: 2, awayTeamScore: 3);
match.complete(scoring);
match.outcome(); //-> MatchOutcome.AwayTeamWon
As you can see, the model should be a solution to a well-defined problem. It should model the reality of that problem (not the real world), no more, no less.
I would like to use the rand() function for match ID can I do this
inside the setter function of setMatchId or should it be done outside
of the class?
The generation of an entity's identity is usually not the responsibility of the identity itself in respect to the Single Responsibility Principle. The algorithm that generates the identity may change independently of the Match concept itself.
First of all, there's nothing bad in having several get/set methods, unless you're coding on a 64kb RAM machine (Where you probably would use C, Lua, or such instead of PHP). If they're all doing (almost) the same thing and you think they're messing code up, put them on the very end of your class, so they don't block your vision ;-).
For the practical altering of your code:
If you have several members which differ only by data but actually represent the same kind, like team1, team2 puting them into an array and use a get/setByIndex is legit.
(Take care: I didn't use PHP for hundreds of years or so, there might me syntactical mistakes)
Example:
function setTeamByIndex($pIndex, $pTeam){
$this->teams[$pIndex] = $pTeam;
}
function getTeamByIndex($pIndex){
return $this->team[$pIndex];
}
Alternatively, in other language it's common to return multiple values. This is not possible in PHP, but there's a workaround:
setTeamsFromArray
-- receives an Array with teams and applies the given teams by their key.
getAllTeamsArray
-- returns an Array, containing all teams.
function setTeamsFromArray($pTeams){
foreach ($pTeams as $key=>$team) {
$this->teams[$key] = $team
}
}
function getAllTeamsArray(){
return array( $this->team1, $this->team2 )
}
echo(getAllTeamsArray()[0]) -> echos team1
echo(getAllTeamsArray()[1]) -> echos team2
In my opinion, this is all one reasonable could do in your case.
Shrinking stuff down is not always reasonable and 10 4liners are, most of the time, better than 1 40liner.
for geter and seter you can use __call() magic method for example realize the geters and setters
public function __call($name, $arguments)
{
// TODO: Implement __call() method.
$method = substr($name,0,3);
$key = strtolower(substr($name,3,strlen($name)));
if($method == 'set') {
$this->_data[$key] = $argument[0]
return $this;
} elseif($method=='get') {
if(isset($this->_data[$key])) {
return $this->_data[$key];
} else {
return null;
}
}
}
this is simple realization getter and setter automaticaly generate.
Related
First, take a look at this PHP 5.5.8 code which implements lazy initialization of class properties with using a Trait:
trait Lazy
{
private $__lazilyLoaded = [];
protected function lazy($property, $initializer)
{
echo "Initializer in lazy() parameters has HASH = "
. spl_object_hash($initializer) . "\n";
if (!property_exists($this, $property)
|| !array_key_exists($property, $this->__lazilyLoaded))
{
echo "Initialization of property " . $property . "\n";
$this->__lazilyLoaded[$property] = true;
$this->$property = $initializer();
}
return $this->$property;
}
}
class Test
{
use Lazy;
private $x = 'uninitialized';
public function x()
{
return $this->lazy('x', function(){
return 'abc';
});
}
}
echo "<pre>";
$t = new Test;
echo $t->x() . "\n";
echo $t->x() . "\n";
echo "</pre>";
The output is as follow:
uninitialized
Initializer in lazy() parameters has HASH = 000000001945aafc000000006251ed62
Initialization of property x
abc
Initializer in lazy() parameters has HASH = 000000001945aafc000000006251ed62
abc
Here are my questions and things I'd like to discuss and improve, but I don't know how.
Based on the HASH values reported, it may appear that the initializer function is created only once.
But actually uniqueness is not guaranteed between objects that did not reside in memory simultaneously. So the question remains unanswered - whether the initializer gets created only once, and it matters for performance I think, but I'm not sure.
The way it's implemented now is not very safe in that if I refactor the code and change property $x to something else, I might forget to change the 'x' value as a first parameter to lazy() method. I'd be happy to use & $this->x instead as a first parameter, but then inside lazy() function I don't have a key to use for $__lazilyLoaded array to keep track of what has been initialized and what has not. How could I solve this problem? Using hash as a key isn't safe, nor it can be generated for callbacks like array($object, 'methodName')
If $this->x is a private property, it's safe for outer world to call the x() method, but for the class' methods it's still unsafe to access the raw $this->x property as it can be still uninitialized. So I wonder is there a better way - maybe I should save all the values in some Trait's field?
The global aim is to make it:
a) Fast - acceptable enough for small and medium software applications
b) Concise in syntax - as much as possible, to be used widely in the methods of the classes which utilize the Lazy trait.
c) Modular - it would be nice if objects still held their own properties; I don't like the idea of one super-global storage of lazily-initialized values.
Thank you for your help, ideas and hints!
So the question remains unanswered - whether the
initializer gets created only once, and it matters for performance I
think, but I'm not sure.
Well, closure instance is created only once. But anyway, performance will depend not on closure instance creation time (since it is insignificant), but closure execution time.
I'd be happy to use & $this->x instead as a first parameter, but then
inside lazy() function I don't have a key to use for $__lazilyLoaded
array to keep track of what has been initialized and what has not. How
could I solve this problem? Using hash as a key isn't safe, nor it can
be generated for callbacks like array($object, 'methodName')
I can propose the following solution:
<?php
trait Lazy
{
private $_lazyProperties = [];
private function getPropertyValue($propertyName) {
if(isset($this->_lazyProperties[$propertyName])) {
return $this->_lazyProperties[$propertyName];
}
if(!isset($this->_propertyLoaders[$propertyName])) {
throw new Exception("Property $propertyName does not have loader!");
}
$propertyValue = $this->_propertyLoaders[$propertyName]();
$this->_lazyProperties[$propertyName] = $propertyValue;
return $propertyValue;
}
public function __call($methodName, $arguments) {
if(strpos($methodName, 'get') !== 0) {
throw new Exception("Method $methodName is not implemented!");
}
$propertyName = substr($methodName, 3);
if(isset($this->_lazyProperties[$propertyName])) {
return $this->_lazyProperties[$propertyName];
}
$propertyInializerName = 'lazy' . $propertyName;
$propertyValue = $this->$propertyInializerName();
$this->_lazyProperties[$propertyName] = $propertyValue;
return $propertyValue;
}
}
/**
* #method getX()
**/
class Test
{
use Lazy;
protected function lazyX() {
echo("Initalizer called.\r\n");
return "X THE METHOD";
}
}
echo "<pre>";
$t = new Test;
echo $t->getX() . "\n";
echo $t->getX() . "\n";
echo "</pre>";
Result:
c:\Temp>php test.php
<pre>X THE METHOD
X THE METHOD
</pre>
c:\Temp>php test.php
<pre>Initalizer called.
X THE METHOD
X THE METHOD
</pre>
c:\Temp>
You cannot always be protected from forgetting something, but it is easier to remember when all things are close to each other. So, I propose to implement lazy loaders as methods on corresponding classes with specific names. To provide autocomplete #method annotation can be used. In a good IDE refactoring method name in annotation will allow to rename method across all project. Lazy loading function will be declared in the same class so renaming it also is not a problem.
By declaring a function with a name, starting with "lazy", in my example you both declare a corresponding accessor function, with name starting with "get" and it's lazy loader.
If $this->x is a private property, it's safe for outer world to call the x() method, but for the class' methods it's still unsafe to
access the raw $this->x property as it can be still uninitialized. So
I wonder is there a better way - maybe I should save all the values in
some Trait's field?
Trait fields are available in the class, that uses specific trait. Even private fields. Remember, this is composition, not inheritance. I think it's better to create private trait array field and store your lazy properties there. No need to create a new field for every property.
But I cannot say I like the whole scheme. Can you explain the use of it for you? May be we can come with better solution.
This is a bit philosophical but I think many people encountered this problem. The goal is to access various (dynamically declared) properties in PHP and get rid of notices when they are not set.
Why not to __get?
That's good option if you can declare your own class, but not in case of stdClass, SimpleXML or similar. Extending them is not and option since you usually do not instantiate these classes directly, they are returned as a result of JSON/XML parsing.
Example:
$data = '{"name": "Pavel", "job": "programmer"}';
$object = json_decode($data);
We have simple stdClass object. The problems is obvious:
$b = $data->birthday;
The property is not defined and therefore a notice is raised:
PHP Notice: Undefined property: stdClass::$birthday
This can happen very often if you consider that you get that object from parsing some JSON. The naive solution is obvious:
$b = isset($data->birthday) ? $data->birthday : null;
However, one gets tired very soon when wrapping every accessor into this. Especially when chaining the objects, such as $data->people[0]->birthday->year. Check whether people is set. Check if the first element is set. Check if birthday is set. Check if year is set. I feel a bit overchecked...
Question:
Finally, my question is here.
What is the best approach to this issue? Silencing notices does not seem to be the best idea. And checking every property is difficult. I have seen some solutions such as Symfony property access but I think it is still too much boilerplate. Is there any simpler way? Either third party library, PHP setting, C extension, I don't care as far as it works... And what are the possible pitfalls?
If I understand correctly, you want to deal with 3rd party Objects, where you have no control, but your logic requires certain properties that may not be present on the Object. That means, the data you accepting are invalid (or should be declared invalid) for your logic. Then the burden of checking the validity goes into your validator. Which I hope you already have following best practices to deal with 3rd party data. :)
You can use your own validator or one by frameworks. A common way is to write a set of Rules that your data needs to obey in order to be valid.
Now inside your validator, whenever a rule is not obeyed, you throw an Exception describing the error and attaching Exception properties that carry the information you want to use. Later when you call your validator somewhere in your logic, you place it inside try {...} block and you catch() your Exceptions and deal with them, that is, write your special logic reserved for those exceptions. As general practice, if your logic becomes too large in a block, you want to "outsource" it as function.
Quoting the great book by Robert Martin "Clean Code", highly recommended for any developer:
The first rule of function is that they should be small. The second is that they should be smaller than that.
I understand your frustration dealing with eternal issets and see as cause of the problem here that each time you need to write a handler dealing with that technical issue of this or that property not present. That technical issue is of very low level in your abstraction hierarchy, and in order to handle it properly, you have to go all the way up your abstraction chain to reach a higher step that has a meaning for your logic. It is always hard to jump between different levels of abstraction, especially far apart. It is also what makes your code hard to maintain and is recommended to avoid.
Ideally your whole architecture is designed as a tree where Controllers sitting at its nodes only know about the edges going down from them.
For instance, coming back to your example, the question is -
Q - What is the meaning for your app of the situation that $data->birthday is missing?
The meaning will depend on what the current function throwing the Exception wants to achieve. That is a convenient place to handle your Exception.
Hope it helps :)
One solution (I don't know if it's the better solution, but one possible solution) is to create a function like this:
function from_obj(&$type,$default = "") {
return isset($type)? $type : $default;
}
then
$data = '{"name": "Pavel", "job": "programmer"}';
$object = json_decode($data);
$name = from_obj( $object->name , "unknown");
$job = from_obj( $object->job , "unknown");
$skill = from_obj( $object->skills[0] , "unknown");
$skills = from_obj( $object->skills , Array());
echo "Your name is $name. You are a $job and your main skill is $skill";
if(count($skills) > 0 ) {
echo "\n\nYour skills: " . implode(",",$skills);
}
I think it's convienent because you have at the top of your script what you want and what it should be (array, string, etc)
EDIT:
Another solution. You could create a Bridge class that extends ArrayObject:
class ObjectBridge extends ArrayObject{
private $obj;
public function __construct(&$obj) {
$this->obj = $obj;
}
public function __get($a) {
if(isset($this->obj->$a)) {
return $this->obj->$a;
}else {
// return an empty object in order to prevent errors with chain call
$tmp = new stdClass();
return new ObjectBridge($tmp);
}
}
public function __set($key,$value) {
$this->obj->$key = $value;
}
public function __call($method,$args) {
call_user_func_array(Array($this->obj,$method),$args);
}
public function __toString() {
return "";
}
}
$data = '{"name": "Pavel", "job": "programmer"}';
$object = json_decode($data);
$bridge = new ObjectBridge($object);
echo "My name is {$bridge->name}, I have " . count($bridge->skills). " skills and {$bridge->donald->duck->is->paperinik}<br/>";
// output: My name is Pavel, I have 0 skills and
// (no notice, no warning)
// we can set a property
$bridge->skills = Array('php','javascript');
// output: My name is Pavel, my main skill is php
echo "My name is {$bridge->name}, my main skill is {$bridge->skills[0]}<br/>";
// available also on original object
echo $object->skills[0]; // output: php
Personally I would prefer the first solution. It's more clear and more safe.
Data formats which have optional fields are quite difficult to deal with. They're problematic in particular if you have third parties accessing or providing the data, since there rarely is enough documentation to comprehensively cover all causes for the fields to appear or disappear. And of course, the permutations tend to be harder to test, because coders won't instinctively realize that the fields may be there.
That's a long way of saying that if you can avoid having optional fields in your data, the best approach to dealing with missing object properties in PHP is to not have any missing object properties...
If the data you're dealing with is not up to you, then I'd look into forcing default values on all fields, perhaps via a helper function or some sort of crazy variation of the prototype pattern. You could build a data template, which contains default values for all fields of the data, and merge that with the real data.
However, if you do that, are you failing, unless? (Which is another programming philosophy to take into heart.) I suppose one could make the case that providing safe default parameters satisfies data validation for any missing fields. But particularly when dealing with third party data, you should exercise high level of paranoia against any field you're plastering with default values. It's too easy to just set it to null and -- in the process -- fail to understand why it was missing in the first place.
You should also ask what are you trying to achieve? Clarity? Safety? Stability? Minimal code duplication? These are all valid goals. Being tired? Less so. It suggests a lack disciprine, and a good programmer is always disciprined. Of course, I'll accept that people are less likely to do something, if they view it as a chore.
My point is, the answer to your question may differ depending on why it's being asked. Zero effort solution is probably not going to be available, so if you're only exchanging one menial programming task to another one, are you solving anything?
If you are looking for a systematic solution that will guarantee that the data is always in the format you have specified, leading to reduced number of logical tests in the code that processes that data, then perhaps what I suggested above will be of help. But it will not come without a cost and effort.
in PHP version 8
you can use Nullsafe operator as follow:
$res = $data?->people[0]?->birthday?->year;
The best answers have been given, but here is a lazy one:
$data = '{"name": "Pavel", "job": "programmer"}';
$object = json_decode($data);
if(
//...check mandatory properties: !isset($object->...)&&
){
//error
}
error_reporting(E_ALL^E_NOTICE);//Yes you're right, not the best idea...
$b = $object->birthday?:'0000-00-00';//thanks Elvis (php>5.3)
//Notice that if your default value is "null", you can just do $b = $object->birthday;
//assign other vars here
error_reporting(E_ALL);
//Your code
Use a Proxy object - it will add just one tiny class and one line per object instantiation to use it.
class ProxyObj {
protected $obj;
public function __construct( $obj ) {
$this->_obj = $obj;
}
public function __get($key) {
if (isset($this->_obj->$key)) {
return $this->_obj->$key;
}
return null;
}
public function __set($key, $value) {
$this->_obj->$key = $value;
}
}
$proxy = new ProxyObj(json_decode($data));
$b = $proxy->birthday;
You can decode the JSON object to an array:
$data = '{"name": "Pavel", "job": "programmer"}';
$jsonarray = json_decode($data, true);
$b = $jsonarray["birthday"]; // NULL
function check($temp=null) {
if(isset($temp))
return $temp;
else
return null;
}
$b = check($data->birthday);
I've hit this problem, mainly from getting json data from a nosql backed api that by design has inconsistent structures, eg if a user has an address you'll get $user->address otherwise the address key just isn't there. Rather than put tons of issets in my templates I wrote this class...
class GracefulData
{
private $_path;
public function __construct($d=null,$p='')
{
$this->_path=$p;
if($d){
foreach(get_object_vars($d) as $property => $value) {
if(is_object($d->$property)){
$this->$property = new GracefulData($d->$property,$this->_path . '->' . $property);
}else{
$this->$property = $value;
}
}
}
}
public function __get($property) {
return new GracefulData(null,$this->_path . '->' . $property);
}
public function __toString() {
Log::info('GracefulData: Invalid property accessed' . $this->_path);
return '';
}
}
and then instantiate it like so
$user = new GracefulData($response->body);
It will gracefully handle nested calls to existing and non existing properties. What it can't handle though is if you access a child of an existing non-object property eg
$user->firstName->something
Lots of good answers here, I consider #Luca 's answer as one of the best - I extended his a little so that I could pass in either an array or object and have it create an easy to use object. Here's mine:
<?php
namespace App\Libraries;
use ArrayObject;
use stdClass;
class SoftObject extends ArrayObject{
private $obj;
public function __construct($data) {
if(is_object($data)){
$this->obj = $data;
}elseif(is_array($data)){
// turn it into a multidimensional object
$this->obj = json_decode(json_encode($data), false);
}
}
public function __get($a) {
if(isset($this->obj->$a)) {
return $this->obj->$a;
}else {
// return an empty object in order to prevent errors with chain call
$tmp = new stdClass();
return new SoftObject($tmp);
}
}
public function __set($key, $value) {
$this->obj->$key = $value;
}
public function __call($method, $args) {
call_user_func_array(Array($this->obj,$method),$args);
}
public function __toString() {
return "";
}
}
// attributions: https://stackoverflow.com/questions/18361594/how-to-solve-the-missing-object-properties-in-php | Luca Rainone
I have written a helper function for multilevel chaining, for example, let's say you want to do something like $obj1->obj2->obj3->obj4, and my helper will return empty string if one of the tiers is not defined or null
class MyUtils
{
// for $obj1->obj2->obj3: MyUtils::nested($obj1, 'obj2', 'obj3')
// returns '' if some of tiers is null
public static function nested($obj1, ...$tiers)
{
if (!isset($obj1)) return '';
$a = $obj1;
for($i = 0; $i < count($tiers); $i++){
if (isset($a->{$tiers[$i]})) {
$a = $a->{$tiers[$i]};
} else {
return '';
}
}
return $a;
}
}
When using nested objects (ObjTwo as a property of objOne):
$objOne->property = new ObjTwo($objOne);
What's the best way to communicate? Here are a few methods I can think of:
Using specific get/set methods
class ObjTwo {
__construct($objOne){
$prop1 = $objOne->get_prop1();
// do something with prop1
$prop2 = $objOne->get_prop2();
// do something with prop2
// ... Having to write all these out is kind of a pain
// if you're going to have 20+ vars, and there's no
// easy way to loop through them.
}
}
The problem: Writing these out line by line, and having to update it when I add new properties.
I know that having a get/set method for each property is recommended, however I'd really like to loop through the data...
How about get_object_vars()
class ObjTwo {
__construct($objOne){
extract(get_object_vars($objOne));
// do something with the vars
}
}
The problem: This method bypasses the ability to use getter/setter methods, and each property would have to be public to be accessible.
Dynamic getter/setter method calls
Another way I have considered is to create an array of fields, and have a strict policy of naming the getter/setter methods:
class ObjTwo {
__construct($objOne){
$prop_array = array('prop1', 'prop2', 'prop_three');
$values = array();
foreach ($prop_array as $prop){
$values[$prop] = $objOne->get_{$prop}();
}
}
}
The problem: Every time I add a new property, I have to make sure to name the get_method() correctly, and update the $prop_array.
Anyone have any better solutions? Maybe just building an array of data?:
$objOne->property = new ObjTwo($objOne->get_data());
I like this solution
Having thought this through, here's a little clarification: I'm not trying to just make identical copies from parent to child or vice-versa - I edited the above examples to show that a little better. It's more just the idea of passing a subset of the object's data from one place to another.
Instead of having to write:
$first_name = $this->member->get_first_name();
$last_name = $this->member->get_last_name();
$email = $this->member->get_email();
$display_name = $this->member->get_display_name();
// etc... and
$this->member->set_first_name($first_name);
$this->member->set_last_name($last_name);
$this->member->set_email($email);
$this->member->set_display_name($display_name);
// etc..
How about having a $this->member->get_fields('first_name', 'last_name', 'email', 'display_name'); method? I don't like having to remember the field names exactly (fname, f_name, first_name, etc), so you could use class constants:
$data = $this->member->get_fields(array(
Member::FIRST_NAME, Member::LAST_NAME, Member::EMAIL, Member::DISPLAY_NAME
));
This way, I can loop through the returned data.
foreach ($data as $key=>$value) // ...
And setting the fields...
$this->member->set_fields(array(
Member::FIRST_NAME => $first_name, // THE BIG ADVANTAGE HERE:
Member::LAST_NAME => $last_name, // These field keys auto-complete
Member::EMAIL => $email, // so you don't have to remember them!
Member::DISPLAY_NAME => $display_name,
// etc...
));
Still thinking this through... any thoughts?
I think you're asking the wrong question. Furthermore I think it would only be possible to really help, if you provided a real example instead of those pseudo examples. Every real situation is different with regard to the proper solution.
Generally all your proposals smell. It seems that what you need is not injection but inheritance. If your 'child' class really needs access to all or most of the properties of another class, it seems it should extend that class.
The parts of your software should know as little as possible about each other. In your comment you mention that you have a Member class and a Form class. I don't know why any of them should know anything about the other at all.
Furthermore you seem to be under the impression that you need to map every property to a property in the new class. Why? If you pass an instance of a class into another class via custructor (= dependency injection) then you can map that instance to a property and then access all properties of the injected instance via that instance. No mapping needed.
class Consumer
{
private $injectedClass;
function __construct($injectedClass)
{
$this->injectedClass = $injectedClass;
}
public function someFunction()
{
//do something by using any property of the injected class
$this->injectedClass->getProperty();
}
}
I tend to do it like this. This may not be the best way to do it:
class Parent_Obj {
$var1; // explanation
$var2; // explanation
$var3; // explanation
$child_obj; // explanation
__construction() {
/* Do a bunch of stuff */
$this->$child_obj = new Child_Obj ($this);
}
}
class Child_Obj {
$var1; // explanation
$var2; // explanation
$var3; // explanation
$parent_obj; // explanation
__construction($parent) {
/* Do a bunch of stuff */
$this->$parent_obj = $parent;
}
/* Some function that needs a method or property of the parent object */
function some_function () {
/* do some stuff */
echo $this->parent_obj->var1; // echo a property of the parent obj
}
}
I believe the term for this is called "aggregation".
I am learning PHP (constantly) and I created some time ago a class that handles translations. I want to emulate gettext but getting the translated strings from a database. However, now that I see it again, I don't like that, being a class, to call it I need to use $Translate->text('String_keyword');. I wouldn't like either to have to use $T->a('String_keyword'); since that's completely not intuitive.
I've been thinking a lot on how to make it to be called with a simple _('String_keyword'), gettext style but, from what I've learned from SO, I haven't been able to find a 'great' way to accomplish this. I need to pass somehow the default language to the function, I don't want to pass it every time I call it as it would be _('String_keyword', $User->get('Language'))). I also don't want to include the user-detection script in the _() function, as it only needs to be run once and not every time.
The easiest one would be to use GLOBALS, but I've learned here that they are completely-utterly forbidden (could this be the only case where I can use them?), then I thought to DEFINE a variable with the user's language like define ( USER_LANGUAGE , $User->get('Language') ), but it seems just to be the same as a global. These are the 2 main options I can see, I know there are some other ways like Dependency Injection but they seem to add just too much complication for a so simple request and I haven't yet had time to dig into them.
I was thinking about creating a wrapper first to test it out. Something like this:
function _($Id, $Arg = null)
{
$Translate = new Translate (USER_LANGUAGE);
return $Translate -> text($Id, $Arg)
}
Here is the translation code. The language is detected before and passed to the object when created.
// Translate text strings
// TO DO: SHOULD, SHOULD change it to PDO! Also, merge the 2 tables into 1
class Translate
{
private $Lang;
function __construct ($Lang)
{
$this->Lang = $Lang;
}
// Clever. Adds the translation so when codding I don't get annoyed.
private function add ($Id, $Text)
{
$sql="INSERT INTO htranslations (keyword, en, page, last) VALUES ('$Id', '$Text', '".$_SERVER['PHP_SELF']."', now())";
mysql_query($sql);
}
private function retrieve ( $Id )
{
$table = is_int ($Id) ? "translations" : "htranslations"; // A small tweak to support the two tables, but they should be merged.
$results = mysql_query ("SELECT ".mysql_real_escape_string($this->Lang)." FROM ".$table." WHERE keyword='".mysql_real_escape_string($Id)."'");
$row = mysql_fetch_assoc ($results);
return mysql_num_rows ($results) ? stripslashes ($row[$this->Lang]) : null;
}
// If needed to insert a name, for example, pass it in the $Arg
public function text($Id, $Arg = null)
{
$Text = $this->retrieve($Id);
if (empty($Text))
{
$Text = str_replace("_", " ", $Id); // If not found, replace all "_" with " " from the input string.
$this->add($Id, $Text);
}
return str_replace("%s", $Arg, $Text); // Not likely to have more than 2 variables into a single string.
}
}
How would you accomplish this in a proper yet simple (for coding) way? Are any of the proposed methods valid or can you come with a better one?
If the problem is simply that
$Translate->text('String_keyword');
feels to long, then consider making the Translate object into a Functor by implementing __invoke:
class Translate
{
// all your PHP code you already have
public function __invoke($keyword, $Arg = null)
{
return $this->text($keyword, $Arg)
}
}
You can then instantiate the object regularly with all the required dependencies and settings and call it:
$_ = new Translate(/* whatever it needs */);
echo $_('Hallo Welt');
That would not introduce the same amount of coupling and fiddling with the global scope as you currently consider to introduce through a wrapper function or as the Registry/Singleton solution suggested elsewhere. The only drawback is the non-speaking naming of the object variable as $_().
I would use a registry or make Translate a singleton. When i first initalize it i would pass in the language which would be dont in the bootstrap phase of the request. Then i would add methods to change the language later if necessary.
After doing that your function becomes pretty simple:
// singleton version
function _($id, $arg = null) {
return Translate::getInstance()->text($id, $arg);
}
// registry version
function _($id, $arg = null) {
return Registry::get('Translate')->text($id, $arg);
}
And then in your bootstap phase you would do something like:
$lang = get_user_lang(); // replace with however you do this
//registry version
Registry::set('Tranlaste', new Translate($lang));
// or the singleton version
// youd use create instance instead of getInstance
// so you can manage the case where you try to call
// getInstance before a language is set
Translate::createInstance($lang);
When we take a look at Javascript frameworks like Dojo, Mootools, jQuery, JS Prototype, etc. we see that options are often defined through an array like this:
dosomething('mainsetting',{duration:3,allowothers:true,astring:'hello'});
Is it a bad practice to implement the same idea when writing a PHP class?
An example:
class Hello {
private $message = '';
private $person = '';
public function __construct($options) {
if(isset($options['message'])) $this->message = $message;
if(isset($options['person'])) $this->person = $person;
}
public function talk() {
echo $this->person . ' says: ' . $this->message;
}
}
The regular approach:
class Hello {
private $message = '';
private $person = '';
public function __construct() {}
public function setmessage($message) {
$this->message = $message;
}
public function setperson($person) {
$this->person = $person;
}
public function talk() {
echo $this->person . ' says: ' . $this->message;
}
}
The advantage in the first example is that you can pass as much options as you want and the class will only extract those that it needs.
For example, this could be handy when extracting options from a JSON file:
$options = json_decode($options);
$hello = new Hello($options);
This is how I do this regulary:
$options = json_decode($options);
$hello = new Hello();
if(isset($options['message'])) $hello->setmessage($options['message']);
if(isset($options['person'])) $hello->setperson($options['person']);
Is there a name for this pattern and do you think this is a bad practice?
I have left validation etc. in the examples to keep it simple.
There are good and bad aspects.
The good:
No need for multiple method signatures (i.e. overloading, where supported)
In keeping with the previous point: methods can be invoked with arguments in any order
Arguments can be dynamically generated, without needing to specify each one that will be present (example: you dynamically create an array of arguments based on user input and pass it to the function)
No need for "boilerplate" methods like setName, setThis, setThat, etc., although you might still want to include them
Default values can be defined in the function body, instead of the signature (jQuery uses this pattern a lot. They frequently $.extend the options passed to a method with an array of default values. In your case, you would use array_merge())
The bad:
Unless you properly advertise every option, your class might be harder to use because few will know what options are supported
It's one more step to create an array of arguments when you know ahead of time which you will need to pass
It's not always obvious to the user that default values exist, unless documentation is provided or they have access to the source code
In my opinion, it's a great technique. My favorite aspect is that you don't need to provide overloaded methods with different signatures, and that the signature isn't set in stone.
There's nothing wrong with that approach, especially if you have a lot of parameters you need to pass to a constructor. This also allows you to set default values for them and array_merge() them inside a constructor (kinda like all jQuery plugins do)
protected $default_params = array(
'option1' => 'default_value'
);
public function __construct($params = array()) {
$this->params = array_merge($this->default_params, $params);
}
If you want live examples of this "pattern", check out symfony framework, they use it almost every where: here's an example of sfValidatorBase constructor
When you give the arguments names it's called "Named Notation" v.s. "Positional Notation" where the arguments must be in a specific order.
In PHP you can pass an "options" parameter to give the same effect as other languages (like Python) where you can use a genuine Named Notation. It is not a bad practice, but is often done where there is a good reason to do it (i.e. in your example or a case where there are lots of arguments and they do not all need to set in any particular order).
I don't know the name, but i really doubt it is a bad practice, since you usally use this when you wan't to declare a small o quick function or class property
If there are mandatory options, they should be in the constructor's parameter list. Then you add the optional options with default values.
public function __construc($mandatory1, $mandatory2, $optional1="value", $optional2="value") { }
If all of your options are optional, then it can be useful to create a constructor taking an array. It would be easier to create the object than with a "normal constructor" : you could provide just the options you want, while with a "normal constructor" if you want to provide $optional2, you have to provide $optional1 (even setting it to the default value).
I wouldn't say its bad practice, at least if you trust the source of the data.
Another possibility would be dynamically calling the setters according to the options array key, like the following:
public function __construct($options) {
foreach($options as $option => $value) {
$method = 'set'.$option;
if(method_exists($this, $method)
call_user_func(array($this, $method, $value);
}
}
Why not do both? Have your constructor cake and eat it too with a static factory "named constructor":
$newHello = Hello::createFromArray($options);
You first have your constructor with the options in order. Then add a static method like this to the same class:
public static function createFromArray($options){
$a = isset($options['a']) ? $options['a'] : NULL;
$b = isset($options['b']) ? $options['b'] : NULL;
$c = isset($options['c']) ? $options['c'] : NULL;
return new Hello($a, $b, $c);
}
This will keep new developers and IDE's happy as they can still see what it takes to construct your object.
I agree with the general attitude of the answers here in that either way is a viable solution depending on your needs and which is more beneficial for your app.