What can I use other than if(!empty( $product->a_funky_function() )) to check if the method is empty before calling it?
I've tried method_exists() and function_exists() and a whole plethora of conditions. I think the issue is that I need to have my $product variable there.
Please help.
A fairly common pattern is to have two methods on your class, along the lines of getField and hasField. The former returns the value, and the latter returns true or false depending whether or not the value is set (where "set" can mean not null, or not empty, or whatever else you might want it to mean).
An example:
class Foo
{
/** #var string */
private $field;
/**
* #return string
*/
public function getField()
{
return $this->field;
}
/**
* #return bool
*/
public function hasField()
{
return $this->getField() !== null;
}
}
This would then be used like:
if ($foo->hasField()) {
$field = $foo->getField();
...
}
Often, like in this example, the has... method often just delegates to the get... method internally, to save duplicating the logic. If the getter contains particularly heavy processing (e.g. a database lookup or API call), you might want to factor in ways to avoid performing it twice, but that's a bit out of scope.
You need absolutely to call it so it can compute the value which it will return, so I think there is no other way to know if it will return something not empty without calling it...
You have to call it.
$result = $product->getFunction();
if(!empty($result)) {
//your code here
}
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.
I have a doctrine entity called Site with the following field:
/**
* #ORM\OneToMany(targetEntity="SiteAsset", mappedBy="site")
*/
protected $assets;
I also have a function for getting an element of $assets with a given value of its url field:
public function getAssetByUrl($url) {
$c = Criteria::create()->
where(Criteria::expr()->eq('url',$url));
$matching = $this->assets->matching($c);
return $matching[0];
}
This function behaves very strangely. It appears to work if I run it immediately after fetching the entity from the database. But after a few database operations have been queued up, it begins to fail. I know it is failing, as I can find the asset I want as follows:
public function getAssetByUrl($url) {
foreach($this->assets as $asset) {
if($asset->getUrl() === $url) {
return $asset;
}
}
}
Furthermore, if I combine the two functions into:
public function getAssetByUrl($url) {
foreach($this->assets as $asset) {
if($asset->getUrl() === $url) {
error_log('found');
}
}
$c = Criteria::create()->
where(Criteria::expr()->eq('url',$url));
$matching = $this->assets->matching($c);
error_log($matching[0] ? 'found' : 'not found');
return $matching[0];
}
Then the Criteria always fails to find a match (i.e., when the function is called it always prints out 'found' followed by 'not found'). This would suggest that Doctrine is failing to find the entity I want in the case when it has already cached the entities in memory.
How can I ensure that matches are always found, while still making use of Doctrine's Criteria filtering system?
The issue was with the following line:
return $matching[0];
This returns the right answer if the PersistentCollection has not yet been fully fetched from the database. This is because the criteria is converted to an SQL query that returns an array containing only matching items; therefore getting the item at index 0 succeeds.
This returns the wrong answer if the PersistentCollection has already been fully fetched. This is because, internally, doctrine calls array_filter on the in-memory collection and returns the result. This means that the first element in the result is not necessarily at index 0, as array_filter preserves keys.
The solution was to wrap $matching in array_values($matching).
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.
I stumbled across this page which talks about the very handy new reflection class that ships with PHP5 and returns all the methods and properties of a class:
print_r to get object methods in PHP?
Following on from this, is there any way to determine the allowable values for the methods it returns?
You mean determine the allowed types of return values, and range of return values, for some method in some class? Very very hardly, I think. After all, there is no way of defining or hinting a return value in PHP. So I could do the following:
class .... {
function myFunction()
{
... some code ....
if (condition) return "A";
.... more code .....
if (condition2) return 2;
.... more code ....
if (condition3) return $_POST["number"];
}
}
this is a totally screwed-up example of course, but you get my point. The possible types of the return value are extremely hard to predict because I could return anything at any point.
I think the best one can do is solve this in documentation blocks. If you follow phpDoc notation:
/**
* #desc Searches for a record.
* #return int the number of found records.
*/
function myMethod()
{ ....
many IDEs are able to at least give you a hint about the expected return type when you type a call to the method.
Well, it depends on what you mean by "allowable values". If it's available in the doc-block, you can probably find it with Reflection... To find the return value:
class foo {
/**
* #returns boolean When false
*/
public function bar($x, $y = 'bar') {
}
}
$reflector = new ReflectionMethod('foo', 'bar');
$comment = $reflector->getDocComment();
if (preg_match('##returns (\\S+)#', $comment, $match)) {
echo "Method returns: ".$match[1];
}
produces:
Method Returns: boolean
Then, you'd just need to parse out what you want from that doc comment... Note it can be a type OR a class (or a grouping of multiple, boolean|null|string|DOMNode)...