When avoiding nulls in PHP, can I reimplement isset? - php

I'm running into the problem that I think everyone runs into with null: that is, there are different kinds of null. It could mean "empty", "unchanged", "unset", "unknown", or any number of things. And I am to the point where I need to distinguish between them somehow.
Basically I have a database manager part of my program that receives an object representing data to be updated in the database from from another part of my program that is responsible for validating form data and converting it into said object (Which has one of a several different classes with specific predefined properties). I need some way to distinguish between a "null" property in that object which means "I actually want the value null stored in the database" and a different type of "null" which means "If there is an existing value in the database don't change it". Of the two, the latter is going to be by far the more common case, but I need some way to allow for the former as well.
My current thought, which would seem the best for this situation, is to create a new class which represents each "type" of null, for example an Unchanged class verses a SetToNull type or something like that. Then I could set the property to an instance of one of those classes.
The only issue is that I would like a function that behaves similar to the existing isset() function in that it would allow me to check both if a given property exists and also if it has a real value (as opposed to one of my new null types) in one simple statement.
I'm aware I can do something like this:
if (isset($thing->property) && !($thing->property instanceof Unchanged || $thing->property instanceof SetToNull)){
// do whatever
}
However, that's obviously not ideal because it's long and unwieldy, and I don't want to have that everywhere all over my code base. I'd prefer something like this:
if (myCustomIsset($thing->property)){
// do whatever
}
However, isset doesn't seem to be a normal function; somehow it suppresses warnings if the property hasn't been defined or something like that, so I'm not sure how to implement something like that myself.

Personally, I would not change the model. The model should represent the data in the database, and the values of its properties should be the same as the values of the columns those properties represent.
I would look at changing how the model gets its data. It sounds like you're instantiating the model from the form data, which doesn't include values for every property because the form only updates certain properties.
If you instantiate the model instead by selecting it from the database, and then modify it with the form data before validating and saving it, it will have the correct values for every property, and null will unambiguously mean null.

One thing that comes to mind which I have done in the past is two pass in two arguments. The first argument is the object by itself. The second argument is a string that contains the property name.
Here is an example:
function MyCustomIsset($object, $property) {
return isset($object->$property) && !($object->$property instanceof Unchanged || $object->$property instanceof SetToNull);
}
And here is how you would use it:
if (myCustomIsset($thing, "property")){
// do whatever
}
Since you aren't referencing the property of the object when you pass it in, PHP won't throw an error. In the body of the function, you reference the property using the passed in string argument. You can reference the property using a special feature of PHP called variable variables.

Related

Cannot access variable property with getter method

In PHP, I cannot assign a value to a variable unless I access its property without using a getter method, is it by design or I missed something?
Simply put, when I do $article->content->value[$some_value] = 'hello' it works, but $article->get_value()[$some_value] = 'hello' sets nothing, the array remains empty.
What the get_value does is just return $this->content->value, and when used as a getter, it does what it supposed to do as expected.
I feel like I missed some basic here, if someone could share me why setting value doesn't work, it'll be great.
Unlike objects, arrays aren't returned by reference in PHP, so when you call the getter method, you're getting back a copy.
If you want to modify the object property itself then you can change the method definition to return a reference by prepending the method name with an ampersand, e.g.
public function &getArray()
{
return $this->array;
}
See https://3v4l.org/1YK9H for a demo
I should stress that this is absolutely not a common pattern in PHP, other than perhaps a long way back into the PHP 4 days when OOP was a lot less ubiquitous. I certainly wouldn't expect a class I was using to return arrays by reference, and neither would I recommend anyone else doing it. Note that it's not possible to ask the class for a reference, in order to prevent unwanted modifications to private properties - the class has to define the behaviour.
The PHP documentation has more information about returning by reference here: http://php.net/manual/en/language.references.return.php

How to pass a boolean argument to a function to act as a switch?

My apologies if the title isn't clear, but I find this difficult to describe. Basically, I have a function that looks for an instance of a class (the school kind) given a class ID number and a date. The function can also create a new class instance if desired.
function get_class_instance($class_id, $date, $create)
Inside the function is a database select on the class_instances table using the class_id and date as arguments. If a matching class_instance is found, its ID is returned. If none is found, there's a conditional for the create argument. If it's true, a new class_instance is created using a database insert and its ID is returned. If false, nothing is changed in the database and false is returned.
I'm still a beginner with PHP and coding generally, so I'm thinking there is probably a better way. The issue is that when calling the function, it might not be clear to someone why there is a boolean being passed.
$original_cinstance_id = get_class_instance($original_class_id, $original_date, 1);
Passing boolean flags to functions to make them do two different things instead of just one is considered a Code Smell in Robert Martin's Clean Code book. The suggested better option would be to have two functions get_whatever and create_whatever.
While a Code Smell depends on context, I think the boolean flag smell does apply in this case, because creating something is different from merely reading it. The former is a Command and the latter is a Query. So they should be separated. One changes state, the other doesn't. It makes for better semantics and separation of concerns to split them. Also, it will reduce the Cyclomatic Complexity of the function by one branch, so you will need one unit-test less to cover it.
Quoting https://martinfowler.com/bliki/FlagArgument.html
Boolean arguments loudly declare that the function does more than one thing. They are confusing and should be eliminated.
Quoting http://www.informit.com/articles/article.aspx?p=1392524
My reasoning here is that the separate methods communicate more clearly what my intention is when I make the call. Instead of having to remember the meaning of the flag variable when I see book(martin, false) I can easily read regularBook(martin).
Additional discussion and reading material:
https://softwareengineering.stackexchange.com/questions/147977/is-it-wrong-to-use-a-boolean-parameter-to-determine-behavior
https://medium.com/#amlcurran/clean-code-the-curse-of-a-boolean-parameter-c237a830b7a3
https://8thlight.com/blog/dariusz-pasciak/2015/05/28/alternatives-to-boolean-parameters.html
You can use a default value for create, so if you pass anything to it, it acts as a normal "get" operation for the database. Like this:
function get_class_instance($class_id, $date, $create = false);
You can query your ID's like this:
$class_id = get_class_instance(1, "18-10-2017");
You can then pass "true" to it whenever you need to create it on the database:
$class_id = get_class_instance(1, "18-10-2017", true);
Instead of passing a number value, you can pass a real boolean value like this:
$original_cinstance_id = get_class_instance($original_class_id, $original_date, true);
Also in the declaration of your function, you can specify a default value to avoid to pass the boolean each time:
function get_class_instance($class_id, $date, $create = false)
one option is to create an enumerator like so:
abstract class create_options {
const no_action = 0;
const create = 1;
}
so now your function call would look like this:
$original_cinstance_id = get_class_instance($original_class_id, $original_date, create_options::no_action);
in practice as long as your code is well commented then this isn't a major issue with boolean flags, but if you had a dozen possible options with different results then this could be useful.
As others have mentioned, in many languages you can also make an argument optional, and have a default behavior unless the caller specifically defines that argument.

How to tell if last method in chain?

I've started using method chains in my PHP app:
$object->do()->something();
Specifically with querying databases:
$database->select()->from('mytable');
However at the moment I need to call a function to actually perform the query at the end:
$database->select()->from('mytable')->perform();
This is annoying because usually every chain will perform straight away. Sometimes it wouldn't, but 9 times out of 10 it will. I need this to work with any object, not just a database handler.
Is there a way to tell if the last method I call is the last one in the method itself, so it would perform() without me telling it to?
If not, I might just try to use some keywords like ->now() instead of ->perform()
I have a few idea(s):
each method has a parameter that if set will let it know it's at the end (maybe it can loop through all parameters to find if a special constant was passed?) eg $object->do()->something(now);
In Perl's DBIX::Class they trigger the query upon using the data. So each query chain doesn't return the database object but a DBIX::Class object that wraps around the database table object.
You can possibly try to implement a similar system. So, for example the following:
$resultSet = $database->select()->from('mytable');
does not actually return the table but a resultSet object. And only when you try to do:
$resultSet->next();
will the library actually fetch the data from the database.
Basically, all the accessor methods like first(), last(), all() and next() fetch and cache the data. If the data has already been fetched and is cached in memory then further calls to accessor methods on the resultSet object should just read the hash in memory instead of connecting to the database.
Additional detail:
Inserts don't have complex clauses like select. So the inserting function doesn't need to be chainable. So for inserts, you can simply have the API do something like this:
$database->insert()->into('mytable')->values({
foo => "hello",
bar => "world"
});
where the values() method act as the inserting function and triggers the database query.
Updates can simply be done on the row object. You can have the API do something like this:
$row = $database->select()->from('mytable')->first();
$row->foo('goodbye');
$row->update();
or alternatively:
$row->update({
foo => "goodbye"
});
And have the update() method trigger the database query.
Using an operator overloading extension, you can specify one of the unary operators for prompting executions
Example:
~$db->select()->from()->where();
If you want to avoid installing an extension, one suggestion I can make is using some kind of escape character in your latest chain call to promp the execution. You should omit it from the final query to avoid syntax errors.
Example:
$db->select()->from()->where("id = 1 ~"); //~ character implies automatic execution
Is there a way to tell if the last method I call is the last one in the method itself
No, I believe there is no such thing. That's because "chain calls" do not differ from "one by one calls". That's just a kind of syntactic sugar for "call => return object => call => return object ..." chain.
I have a few idea(s):
- each method has a parameter that if set will let it know it's at the end (maybe it can
loop through all parameters to find if a special constant was passed?) eg
$object->do()->something(now);
You can achieve an effect of an idea you suggested by overloading __call() method. This way you can abstract your parameter checking logic nicely.
But be aware, that such behaviour is not really obvious architecture-wise and you probably should consider PDO-like approach, that is using "finalizing" method like perform() or do() which, I think, you already have.

Methods and properties

I'm fuzzy on the terminology, and I've tried searching - but have so far not gotten any wiser.
I'm trying to learn OOP in PHP, and I'm trying to write a configuration class that stores value/key pairs in a DB. It's working perfectly well, but not quite the way I want it to work.
Anyway, here's what I do:
$config=new Config();
$keyValue=$config->keyName; // Get key value
$config->keyName=$newKeyvalue; // Set key value
$description=$config->getDescription($keyName); // get description
$config->setDescription($keyName, $description); // set description
Here's what I want to do (only the two last lines, since those are the relevant ones):
$description=$config->keyName->description; // get description
$config->keyName->description=$newDescription; // set description
Setting/getting key values is done with _get()/_set(), but how do I set "subproperties" of a key the same way?
Edit:
Solved it. Working class with _get/_set/_isset/_unset working properly is here: http://pastebin.com/TFAA8Dcq
(Note: Little error checking is done, and this class allows for setting dynamic object names, which is probably not the safest thing in the world)
You would need to make the "keyName" property a class as well. For example, $config->keyName = new stdClass; would allow you to then set $config->keyName->property = "something else";
Obviously you could no longer set the keyName property directly to a string anymore in that case, though. You can, however, implement the __toString magic method in PHP classes to automatically return a string variable when a Class is treated implicitly as a string.

PHP interesting creation of the class instance

I've read about this interesting syntax in PHP:
$value = (new MyClass)->attribute1;
Is it ok to use it? I've never seen anything like this in any code I've analyzed. Any pros and cons?
Why can't I set the attribute using this syntax? Structures like this:
(new MyClass)->attribute1 = 'value1';
throw errors at '=' sign, no matter if the attribute exists in the class already.
Well i don't see the point of using it since you loose your reference to the object, you cannot use it anymore, and it breaks the OO concept.
I think (new MyClass)->attribute1 is resolved first, so it is the same as writing something like 42 = 12
This may have a sense, if the class MyClass supports internal static list (or hashmap) of all existing instances. This way you can create new object and get its unique ID or index in the list for future references (for example, by sending it via cookies to a client).
As for assignment, you'd post exact error message for this case. I can guess, that the error is about assigning something to a temporary value which is about to be destroyed.

Categories