PHP key => value array to method arguments (order?) - php

I think this is quite interesting!!! :).
What I've got?
In the application that I'm using on some level in some objects (doesn't really matter) I get an array, for example:
$array = array(
'argument_label' => 'value_label',
'argument_name' => 'value_name',
'argument_id' => 'value_id'
)
I don't have any impact on how and when this array is created. Next, I've got a method:
public function arrayArgument($array) {
$label = isset($array['argument_label']) ? $array['argument_label'] : 'default_label';
$name = isset($array['argument_name']) ? $array['argument_name'] : 'default_name';
$id = isset($array['argument_id']) ? $array['argument_id'] : 'default_id';
// Do something
return 'something';
}
I really hate it. There is no way of proper documentation for the method arguments (as PHPDocumentator work not so well with arrays), and those issets are driving me crazy. Additionally it is a nightmare for someone who will work with this code in the future, when I will already be a "retired nerd".
What I want?
I want to have a function like that:
public function notArrayArgument(
$id='default_id',
$label='default_label',
$name='default_name'
) {
// Do something
return 'something';
}
What I can do?
When I get array, I can change some code, and make my own method run. So I need some kind of solution to get from here:
$array = array(
'argument_label' => 'value_label',
'argument_name' => 'value_name',
'argument_id' => 'value_id'
)
To here:
notArrayArgument('value_id', 'value_label', 'value_name');
Or here:
notArrayArgument($array['argument_id'], $array['argument_label'], $array['argument_name']);
What are the problems?
This is not template like. The number of variables is always different, the names are always different, and sometimes some of them are passed, sometimes not.
It should work really fast...
Calling the method arguments in the right order. Array can be sorted, not sorted or random sorted, while the arguments inside method are always in the same order. The array should be reordered to match the method arguments order, and after that the method should be called.
What I came with?
I've got an idea using reflectionClass. I can check the names of method arguments, get them in order, reorder the array and try to call this method. But this is quite resource eating solution, as reflectionClass is not so fast I think.
Solution using extract? This would be great. But after extract, I need to use exact variables names in code. And I don't know them as every time they are different, and I need an universal approach.
NEW (thx to comment): call_user_func_array(). It is great, but it only works with indexed arrays, so this will be the last but not least step of the possible solution. As the order of arguments is still unknown...
Does this problem have a nice semantic, pragmatic solution?
I read my question once more, and I hope it is clear to understand. If not, please post a comment and I will do my best to describe the problem better.

Kudos for thinking about the maintainer, but I'd argue simplicity is just as important as nice semantics and pragmatism. Consider this: if you had to ask how to write such a pattern, what are the chances that it will be obvious to the reader? I'd much rather come across code where I can just think "Yep, that's clear" than "Oh cool that's a really intricate and clever way of setting array defaults".
With this in mind, it seems to me that an array is being used in a situation more suited to a class. You have an id, a label and a name, which surely represents an entity - exactly what classes are for! Classes make it easy to set defaults and provide PHPDoc on each of their properties. You could have a constructor that simply takes one of your existing arrays and array_merge()s it with an array of defaults. For the reverse conversion, conveniently, casting an object to an array in PHP results in an associative array of its properties.

Try to use classes as George Brighton mentioned.
If you can't for some legacy or library constraint, you will have to use reflection. Don't worry too much about the performance of reflection classes, a lot of frameworks use them to do the request routing.
You can use a function like:
function arrayArgument($object, $method, $array)
{
$arguments = [];
$reflectionMethod = new ReflectionMethod(get_class($object), $method);
foreach ($reflectionMethod->getParameters() as $parameter)
{
$arguments[] = isset($array[$parameter->name]) ? $array[$parameter->name] : $parameter->getDefaultValue();
}
call_user_func_array(array($object, $method), $arguments);
}
So you can do
$instance = new MyClass();
arrayArgument($instance, 'notArrayArgument', ['name' => 'myname']);

Related

PHP objecture structure chainability

I am trying to build a huge object structure that depends on hundreds of queries. To make it performant i did the following approach:
Each query result object is build into an object. For example a result set from table "apples" is build into an apple object. An orange result is build into an orange object.
Each object is saved in the registry, where the registry is a global array with functions like isExistent($type, $id), add($type, $obj) and get($type, $id). I want to store each object from point 1 once here to access it later without needing to call another query for the object (tons of objects are needed several times at different places).
I want objects to be chained together so i can do something like $tree->getFruits()->getApple()->getPlace()->getName(). I also want objects in my hierarchy to be linked in both directions for later use.
Why do i need this? For example the "apple" object with id 3 will be used once when the $tree object is instantiated, once when the $vitamins object is created, etc. I don't want to sent another query() each time to the database this object is needed. So i check something like:
Registry::isExistent("APPLE", $id)
and then get it from the Registry if it is there, and otherwise call it from db and then additionally add it to registry.
Problem: I am getting infinite loops for obvious reasons, which i could easily fix, but i don't know what's the best approach here.
Actual Code (not example stuff like apples and oranges from above):
$mask = new Mask($row["maskId"], $row["maskName"]);
foreach (Fields::getFieldsFor($mask->getId()) as $field) {
$mask->addField($field);
}
Registry::add("MASK", $mask);
and the getter for a field of a mask:
$mask = Registry::receive("MASK", $row["maskId"]);
$category = Registry::receive("FIELDTYPE", $row["fieldCategory"]);
$field = new Field($row["fieldId"], $category, $row["fieldQuestion"],
$row["fieldDescription"], $row["fieldPosition"], null, null, null, $mask);
Registry::add("FIELD", $field);
This will produce an infinite loop. A mask consists of several fields. When building the $mask object i want to have each field linked in the $mask array. When building each $field object i want to parent $mask linked to it. This will obviously fail because i only add to registry, AFTER they are done building which will obviously fail because before that they will already have a look at the registry.
There are 2 solutions i can think of:
Throw away my chainability and simply store the linked ids in my objects. So instead of storing a reference to the parent mask object in each field i would simply store the id and later retreive the actual object from the registry. This would be the easiest fix, but would throw away the chainability.
Split up the primitive object instantiation and the complex attributes. Just add the primitives via the constructor and add the complex like the actual $mask object to each field after the constructor AND after the Registry::add() via a setter.
What would be best to do here? Is something wrong in my general approach?
My goal simply is to build an easy to work with object structure (will be hundreds of objects) and to reduce redundant queries(). This really needs to be fast. I also only want each different object once and then linked accordingly in the complex object structure.
Thanks and sorry for a long description.
well, i've done something similar for one project few years ago using PHP Magic Method __get(). What i've done actually is storing the Getter Object inside a variable called Registry.get; something like: $this->get = new GetterObject($this);. Now, notice that i passed the Registry as a variable to my GetterObject, you might ask "why?" simply this is:
How it Works
The GetterObject gets initialized with the Registry as a variable (unless Registery as a global absolute object then no need for this!).
You "get" a ORM-object using something like Registery::get->fruits;.
The GetterObject will check the Registry array of objects to find fruits ORM-object.
The GetterObject will return a new instance of itself with some additional variables defining at which step you are; something like $this->path = "/fruits/";.
Now, when you "get" the second object, say, Registery::get->fruits->apples;, the GetterObject will simple check which was the last $this->path piece, on which it will depend to find the child element. Note that this step depends highly on how you structured your own ORM-objects array.
The GetterObject will repeat steps 4, and 5 until you end up with the object of desire and do something like ->return_values(); which is a defined function that returns the actual ORM-object you are looking for, not the GetterObject you were using to navigate through your ORM-objects.
As a small note again, this might not be the best practice for all the Registry-based platforms, so, make sure you really need something this complicated before doing so. Otherwise, a simple __get() definition might all what you need. Best of Luck.

More efficient way to use args in function

I need to define a function in PHP that contains multiple arguments (as shown below):
function the_function($arg1, $arg2, $arg3, $arg4, $arg5, $arg6, $arg7, ...)
but I believe there is a more efficient and clean method of doing this. Can anyone help me? I thought of using an array, something like that. obs: sorry for the noob question! :b
Edit: i'll use something like
function foo(
'number of topics', 'topics per page', 'topics per line'
, 'type of topic', 'number of excerpt words'
);
I think you can go three ways with this:
If all arguments are semantically the same, use func_get_args() within your function to iterate over the given arguments. A single array argument is a respected alternative.
If most arguments are semantically the same, group them together as a single array argument. Treat the other arguments separately.
In all other cases, group related arguments together into a separate object.
They're not entirely mutually exclusive btw.
An array is the standard approach to avoiding a ton of arguments (if a function does truly need that many). Just consider whether or not the type of things in the array are numerical in nature or if naming them makes sense. You may want to pass in an associate array rather than numerical. Of course, be absolutely sure that the function really needs those arguments and can't be broken down further.
Here's just a random example where an associative array might make sense.
$phone = [
'home' => '555-5551',
'cell' => '555-5552',
'work' => '555-5553'
];
function updateUserInfo($userId, $age, array $phone) {
}
rather than:
function updateUserInfo($userId, $age, $homePhone, $cellPhone, $workPhone) {
}
I recommend sticking to declaring your arguments explicitly. It makes for easier testing and less ambiguity. Also, you can enforce some type checking on objects and arrays.
function foo( User $user, array $user_friend_ids);

indexed array foreach shorthand

Data:
$players = array(
new Player('psycketom'),
new Player('stackexchanger'),
new Player('max')
);
Usually, in order to get something out of every object within array, we have to use for / foreach.
foreach ($players as $player)
{
var_dump( $player->score );
}
But, since it's a repetitive task, is there a way to shortcut it to something along these imaginary lines(?):
var_dump( every( $players )->score );
every( $players )->score += 40;
Since I have not seen such a feature for php, is there a way to implement it?
I have asked the question using php as main language, but the language-agnostic and programming-languages stand for the second part of the question: what languages support such or at least similar shorthand?
So, you are correct that PHP does not support this "out of the box" (except kinda, see below). The first language I know of that does is Objective-C (well, at least the CoreFoundation library). NSArrays and other sets have methods to (in one line) instruct that a given method should be executed on all members; and even more cool (to me, at least) is the concept of "keypaths" and the support that NSArray and others has for them. An example; lets say you have an array of "people" who each have a parent, who in turn have a "name":
arrayOfNames = [peopleArray valueForKeyPath:"parent.name"];
arrayOfNames is now an array of all the parents' names.
The closest thing PHP has is array_map, which you can use together with anonymous functions to very quickly whip something together.
edit anecdotal as it may be, one should remember that loop structures don't need their curly-braces if there is only one statement to execute; so any fancier solutions need to compete with this:
foreach($players as $p) $p->score += 40;
And I'll mention a deeper solution for those OOP fans out there... If you work with collection objects instead of arrays, the world is your oyster with stuff like this. The simplest concept that comes to mind is php's magic __call() method. How simple to iterate over your members and make that call for your users? For more controll, you can implement a few different strategies for iteration (one for transforms, one for filters, etc. Difference being what gets returned, essentially). So in theory you could create a few different iterator classes, and in your "main" collection class implement a couple methods to get one of them, which will be pre-initialized with the contents:
$players->transform()->addScore(40);
where transform() returns an instance of your "don't return the array" iterator, which uses the __call() technique.
The sky starts to open up at this point, and you can start to build filter iterators which take predicates and return another collection of a subset of the objects, and syntax like this is possible:
// send flyer to all parents with valid email address
$parentsPredicate = new Predicate('email');
$players->filteredByPredicate($parentsPredicate)->each()->email($fyler_email_body);
You could do:
var_dump(array_map(function($el){return $el->score;}, $players));
array_walk($players, function($el) {$el->score += 40;});

Why would one want to pass primitive-type parameters by reference in PHP?

One thing that's always bugged me (and everyone else, ever) about PHP is its inconsistency in function naming and parameters. Another more recent annoyance is its tendency to ask for function parameters by reference rather than by value.
I did a quick browse through the PHP manual, and found the function sort() as an example. If I was implementing that function I'd take an array by value, sort it into a new array, and return the new value. In PHP, sort() returns a boolean, and modifies the existing array.
How I'd like to call sort():
$array = array('c','a','b');
$sorted_array = sort($array);
How PHP wants me to call sort():
$array = array('c','a','b');
sort($array);
$sorted_array = $array;
And additionally, the following throws a fatal error: Fatal error: Only variables can be passed by reference
sort(array('c','a','b');
I'd imagine that part of this could be a legacy of PHP's old days, but there must have been a reason things were done this way. I can see the value in passing an object by reference ID like PHP 5+ does (which I guess is sort of in between pass by reference and pass by value), but not in the case of strings, arrays, integers and such.
I'm not an expert in the field of Computer Science, so as you can probably gather I'm trying to grasp some of these concepts still, and I'm curious as to whether there's a reason things are set up this way, or whether it's just a leftover.
The main reason is that PHP was developed by C programmers, and this is very much a C-programming paradigm. In C, it makes sense to pass a pointer to a data structure you want changed. In PHP, not so much (Among other things, because references are not the same as a pointer).
I believe this is done for speed-reason.
Most of the time you need the array you are working on to be sorted, not a copy.
If sort should have returned a new copy of the array then for each time you call sort(); the PHP engine should have copied the array into new one (lowering speed and increasing space cost) and you would have no way to control this behaviour.
If you need the original array to be not sorted (and this doesn't happen so often) then just do:
$copy = $yourArray;
sort($yourArray);

Best method of passing/return values

The reason I am asking this question is because I have landed my first real (yes, a paid office job - no more volunteering!) Web Development job about two months ago. I have a couple of associates in computer information systems (web development and programming). But as many of you know, what you learn in college and what you need in the job site can be very different and much more. I am definitely learning from my job - I recreated the entire framework we use from scratch in a MVC architecture - first time doing anything related to design patterns.
I was wondering what you would recommend as the best way to pass/return values around in OO PHP? Right now I have not implement any sort of standard, but I would like to create one before the size of the framework increases any more. I return arrays when more than 1 value needs to get return, and sometimes pass arrays or have multiple parameters. Is arrays the best way or is there a more efficient method, such as json? I like the idea of arrays in that to pass more values or less, you just need to change the array and not the function definition itself.
Thank you all, just trying to become a better developer.
EDIT: I'm sorry all, I thought I had accepted an answer for this question. My bad, very, very bad.
How often do you run across a situation where you actually need multiple return values? I can't imagine it's that often.
And I don't mean a scenario where you are returning something that's expected to be an enumerable data collection of some sort (i.e., a query result), but where the returned array has no other meaning that to just hold two-or-more values.
One technique the PHP library itself uses is reference parameter, such as with preg_match(). The function itself returns a single value, a boolean, but optionally uses the supplied 3rd parameter to store the matched data. This is, in essence, a "second return value".
Definitely don't use a data interchange format like JSON. the purpose of these formats is to move data between disparate systems in an expected, parse-able way. In a single PHP execution you don't need that.
You can return anything you want: a single value, an array or a reference (depending on the function needs). Just be consistent.
But please don't use JSON internally. It just produces unnecessary overhead.
I also use arrays for returning multiple values, but in practice it doesn't happen very often. If it does, it's generally a sensible grouping of data, such as returning array('x'=>10,'y'=>10) from a function called getCoordinates(). If you find yourself doing lots of processing and returning wads of data in arrays from a lot of functions, there's probably some refactoring that can be done to put the work into smaller units.
That being said, you mentioned:
I like the idea of arrays in that to pass more values or less, you just need to change the array and not the function definition itself.
In that regard, another technique you might be interested in is using functions with variable numbers of arguments. It is perfectly acceptable to declare a function with no parameters:
function stuff() {
//do some stuff
}
but call it with all the parameters you care to give it:
$x = stuff($var1, $var2, $var3, $var4);
By using func_get_args(), func_get_arg() (singular) and func_num_args() you can easily find/loop all the parameters that were passed. This works very well if you don't have specific parameters in mind, say for instance a sum() function:
function sum()
{
$out = 0;
for($i = 0; $i < $c = func_num_args(); $i++) {
$out += func_get_arg($i);
}
return $out;
}
//echoes 35
echo sum(10,10,15);
Food for thought, maybe you'll find it useful.
The only thing I'm careful to avoid passing/returning arrays where the keys have "special" meaning. Example:
<?php
// Bad. Don't pass around arrays with 'special' keys
$personArray = array("eyeColor"=>"blue", "height"=>198, "weight"=>103, ...);
?>
Code that uses an array like this is harder to refactor and debug. This type of structure is better represented as an object.
<?php
Interface Person {
/**
* #return string Color Name
*/
public function getEyeColor();
...
}
?>
This interface provides a contract that the consuming code can rely on.
Other than that I can't think of any reason to limit yourself.
Note: to be clear, associative arrays are great for list data. like:
<?php
// Good array
$usStates = array("AL"=>"ALABAMA", "AK"="ALASKA", ... );
?>

Categories