Codeigniter: Not sure how to make calls to this snippet of code - php

I was browsing SO and found this hosted code as a recommended way of cutting down on PHP code.
https://github.com/jamierumbelow/codeigniter-base-model
So far, from the methods that I have figured out how to use, I love what it does and how simple it makes things.
However, in the following code:
/**
* Get a single record by creating a WHERE clause by passing
* through a CI AR where() call
*
* #param string $key The key to search by
* #param string $val The value of that key
* #return object
*/
public function get_by() {
$where =& func_get_args();
$this->_set_where($where);
$this->_run_before_get();
$row = $this->db->get($this->_table)
->row();
$this->_run_after_get($row);
return $row;
}
I'm not exactly sure how to make a call to this function.
The description of what it does is exactly what I want to do.
The #params say it takes in a key and value pair for the WHERE block but I don't see any function inputs in the method signature.
Help, please?

As I'm noticing with a lot of CI code, it's strange and maintenance un-friendly.
PHP functions can accept n or more arguments (where n is the number of arguments defined in the signature)
The code makes use of func_get_args() which returns an array of arguments.
The array of arguments is then passed to the _set_where() method which passes either one or two items to the db->where() method.
A more descriptive method signature would have been
public function get_by($key, $val = null)

For future reference, and like Phil mentioned, the *_by methods pass the value through to the db->where method. This means you can use it in a variety of methods:
$row = $this->model->get_by('key', $value);
Or using an array for multiple WHERE conditions:
$row = $this->model->get_by(array('key' => $value, 'other_key !=' => $value));
Or just the string (don't forget to escape your values!):
$row = $this->model->get_by('some_column = ' . $this->db->escape($value));
Since this question was asked, I've thoroughly updated the documentation so now it should all be a little clearer. Hope this helps.

Related

PHP: Why should only variables be passed by reference?

If you are a PHP developer you most probably have seen the following notice:
Notice: Only variables should be passed by reference in /somefile.php
on line xxx
(Problem extensivley treated in Only variables should be passed by reference)
Example throwing notice:
$string = "hi-dude";
echo end(explode('-', $string));
Working example:
$string = "hi-dude";
$strings = explode('-', $string);
echo end($strings);
Explanation:
Only real variables may be passed by reference, not functions which are returning the correct variable.
However I can not think of a good reason why this notice is happening. It feels unecessary and requires me to write a lot of extra lines of code sometimes. What is the reason for PHP having this strange restriction? Why does this problem even exist?
end() or array_pop() will return the E_NOTICE with message
Only variables should be passed by reference
The reason is that end() requires a reference, because it makes the current element pointer point to the last element.
You can do it with one line,
$string = "this-is-a-sample-text";
echo substr(strrchr($string, '-'), 1);
DEMO: https://3v4l.org/jO29n
Finally I found a great explanation which helped me to understand this: What's the difference between passing by reference vs. passing by value?
As Daniel Pryden states:
In simplest terms:
call by value means that you pass values as function arguments
call by reference means that you pass variables as function arguments
In metaphoric terms:
Call by value is where I write down something on a piece of paper and hand it to you. Maybe it's a URL, maybe it's a complete copy of
War and Peace. No matter what it is, it's on a piece of paper which
I've given to you, and so now it is effectively your piece of paper.
You are now free to scribble on that piece of paper, or use that piece
of paper to find something somewhere else and fiddle with it,
whatever.
Call by reference is when I give you my notebook which has something written down in it. You may scribble in my notebook (maybe I
want you to, maybe I don't), and afterwards I keep my notebook, with
whatever scribbles you've put there. Also, if what either you or I
wrote there is information about how to find something somewhere else,
either you or I can go there and fiddle with that information.
In this case the notice "Only variables should be passed by reference" is still unjustified as we are only interested in retrieving the last value of the array. However the function end() is defined like
mixed end ( array &$array )
The & sign which states passing by reference is there for a certain reason: end() is not just returning the last element of an array, it also changes its internal pointer to the end. Therefore the array is modified.
If we only would return the last element of an array without touching the array there would be no need to pass the array by reference and we would not get this notice. But end() is somehow the wrong function for that.
What if there is no justification for me getting this notice?
Note that also the function to be called might be defined wrong. In my case I hade a function defined like this:
/**
* Flatten an array by one level if only needing a certain key value from a sub array.
*
* Example: [["foo"=>"bar","foo"=>"cheese"]]
* Result: ["bar","cheese"]
*
* #param $array: The input array.
* #param $key: The key to flatupshift. Default is 0.
* #return $array: The result
*/
private function array_flatupshift(&$array, $key = 0) {
$a = [];
foreach ($array as $item) {
if (is_object($item)) {
array_push($a, $item->$key);
} else if (is_array($item)) {
array_push($a, $item[$key]);
}
}
return $a;
}
This is simply a wrong function definition. So if you also get notices like this: Check if the function you call is defined correctly. Passing by reference does not make sense here as the array being passed is not touched in any way. Therefore the function definition should be without the "reference &/":
private function array_flatupshift($array, $key = 0) {
There are some cases where you MIGHT use the error control operator if you know what you are doing. Therefore:
$string = "hi-dude";
echo #end(explode('-', $string));
... would be o.k. I guess is the result of explode is not needed anymore. However notice the drawbacks of suppressing all possible errors. Please correct me if I go wrong here.

PHP: How to document array when they are method parameters [duplicate]

This question already has answers here:
PHPDoc for variable-length arrays of arguments
(7 answers)
Closed 5 years ago.
What's the best way to document the elements of an array when it is a parameter to a method? For example, using the PHPDoc headers, I might have something like:
#param array $data
What this doesn't tell me is what elements are mandatory in the array, and what are optional elements. I imagine this should go in the explanation of the method. Something like:
array: $data
============
int $id Required
name $string Required
town $string Optional
/**
* #param array $data
*
* #var $data[id] int, required
* #var $data[name] string, required
* #var $data[town] string, required
*/
This example of using doctrine and zf2 example:
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue
* #Form\Attributes({"type":"hidden"})
*/
protected $id;
/**
* #ORM\Column(type="string")
* #Form\Type("Zend\Form\Element\Text")
* #Form\Required({"required":"true"})
* #Form\Filter({"name":"StripTags"})
* #Form\Filter({"name":"StringTrim"})
* #Form\Validator({"name":"StringLength", "options":{"min":"5"}})
* #Form\Options({"label":"First name"})
*/
protected $firstName;
If you have such a complex array with constraints for every single member, I wouldn't use an anonymous array but rather a well defined object. With an array, you can never be sure what it holds, that's somewhat like passing "Object" in e.g. Java, which you rarely would consider a good choice.
However, there is the possibility of a little hinting, when you array contains objects of a certain type as explained here, but that's not a really good answer to your question.
If you really need the parameter as an array, you might document it the way you proposed in the method's description; however, if you use an object as parameter, you'd have additional support in modern IDEs (IntelliSense and so on).
EDIT: I mean, for me the question would be "why would I want to use an anonymous array instead of a self defined type" - and besides simplicity (which will backfire as technical debt later if you maintain and extend your code), I cannot think of a reason, especially compared to what you gain in using a user defined type (self documented code, constraints visible and made explicit by standard methods and so on).
If you just need a dump of data, you might want to go with a simple array, but since you're already thinking about optional and required keys, that screams for a user defined type.
EDIT2: Regarding your comment about if your already have an array as source: I'm not sure whether you need to pass it on as an array or do "mapping" operations as soon as you receive the array (e.g. as $_POST or as return value from some third party library or PHP internal functions or such).
I suppose one could argue that it's not the model's business to interpret data generated by views (e.g. HTML forms which POST data), but rather the controller's resonsibility to react accordingly to the input and transfer the model in the appropriate state. What I mean by this is that you could do something like this if you receive e.g. an array as $_POST:
$customer = new Customer();
$customer->setId($_POST['id']);
$customer->setName($_POST['name']);
$customer->setTown($_POST['town']);
And handle errors as soon as you access the $customer, e.g. throwing exceptions if the name is not set (i.e. $_POST['name'] was empty or such). This way, you use the source array to call setters on the object instead of e.g. passing the array to a factory like Customer::buildByHttpPostData(array $data) and thereby delegating the knowledge of the view's details (names of HTML input tags and such).
Bottom line is, there's no "standard" way to declare required or optional array keys, and of course you can describe those constraints in the method description, but perhaps you can circumvent this by keeping with supported ways like PHPDoc comments on setters or getters.
Of course, there may be better ways to approach the problem, and perhaps somebody comes up with a better answer how to handle it.
To answer the question there is no formal way, try to use a way that you think is most intuitive. I do something similar:
/**
* #param array $data [ int $id, string $name, string $town ]
*/
However I wouldn't use this notation for parameters but rather as return values. In your case I would extract the method arguments to an object and pass that into the method instead:
/**
* #param User $user
*/
public function myMethod( User $user )
{
//...
}
The reason for this is the User object exposes it's properties as an API to other developers, self documenting code!
The other method would be to separate out the array elements into arguments like so:
/**
* #param int $id
* #param string $name
* #param string $town
*/
public function myMethod( $id, $name, $town )
{
//...
}
3 arguments is just about passable but you should start looking for a way to refactor it, like my first suggestion. 4 arguments is generally agreed to be messy and you refactor it.

php - multi-dimensional array wrapper class

I have some data represented as multi-dimensional arrays. I would like to embed these data into my OO application and provide extra functionality to these data but also a way to access it both with the traditional random access and using an iterator (so I could use foreach). In other words I would like to implement a wrapping class for a multi-dimensional array.
What I already tried:
I. ArrayAccess
The problem with this one is that the server uses PHP 5.2.17 and &offsetGet() gives an error, thus I can't return by reference, which means I can't implement multidimensional element access:
$obj[$key1][$key2] = $value;
Note, that upgrading to a newer PHP version is currently not an option
II. Using magic methods
This is a bit trickier, but my problems rose when I tried using a variable as key. E.g.
$obj->$key1[$key2] = $value;
The interpreter first evaluated $key1[$key2] throwing a notice and returning the first char of $key1, and uses that as key on my array. I don't know how to solve this one either. I thought of using brackets to force operation priority, but that had the same problem as in my next attempt:
III. Using simple get and set functions
Again, old PHP. It cries when I try to write:
$obj->get($key1)[$key2] = $value;
Because it doesn't know how to index an expression ending in round brackets.
There's still the lost resort option: make the array public and forget OO all together. But before I go there I want to be certain that there's just really no other way of doing this.
Sometimes, it's best shown with an example. For instance, you could have a multidimensional array with ordered quantities of a product, where the first index defines a customer, the second an order and the third the products. The value then is the quantity.
IV. Using simple get and set functions, but with multiple parameters:
$value = $obj->get($key1, $key2);
$obj->set($key1, $key2, $value);
or
$quantity = $orderedQuantities($customerName, $orderNo, $productCode);
The essence of writing an object wrapper, is that it looks like an object and behaves like such. If you implement it so that it looks and behaves like an array, you may just as well use an array. The only reason to wrap an array in an object that behaves like an array is that you can pass it around by reference, without using & parameters.
If that is your goal, just assign the array to $obj, and use $obj->arrayprop[$key1][$key2]. If on the other hand you want it to behave like an object, skip the array syntax altogether.
V. Using nested objects
So using the same example, you can write a customers object (kind of a collection), which embeds order objects, etc..
$quantity = $customers->getCustomerByName($customerName)->getOrder($orderNo)->getProduct($productCode)->quantity;
or, to make it more readable:
$customer = $customers->getCustomerByName($customerName);
$order = $customer->getOrder($orderNo);
$orderLine= $order->getOrderLine($productCode);
$quantity = $product->quantity;
Now, each of these entities are represented by an object. The advantage is that you can easily extend this structure with extra properties. You can give the customer collection 'findByCustomerNo' and 'findByName' methods to find a specific customer, but also a 'filterByPostalCode', which could return a new collection containing only the customers from that postal code.
$order could now contain functions to calculate totals.
$orderLine may contain only a productCode (key) and a quantity, but by making it an object, you can easily add discount, a customized name or description or other properties.
All in all, this structure allows a lot of flexibility, although it needs a little more typing at first and a little getting used to.
Using nested objects can make ArrayAccess work, without passing references. But you can forget about built in array functions completely.
<?php
class ArrObj implements ArrayAccess {
private $arr;
public function __construct($arr = null) {
$this->arr = $arr;
}
public function offsetExists($key) {
return isset($this->arr[$key]);
}
public function offsetGet($key) {
return $this->arr[$key];
}
public function offsetSet($key, $val) {
return $this->arr[$key] = $val;
}
public function offsetUnset($key) {
unset($this->arr[$key]);
}
}
$a = new ArrObj(array(
'foo' => new ArrObj(array(
'bar' => 'qwe'
))
));
echo $a['foo']['bar'] . '<br />';
$a['foo']['bar'] = 'asd';
echo $a['foo']['bar'] . '<br />';
?>
Outputs:
qwe
asd

Is there a way to specify a mandatory array key/index via PHPDoc?

If a function requires an incoming array to have a specific key/index, is there an eloquent way to express it via comments, specifically PHPDoc?
For example:
/**
* Just an example function
* #param array $arr My Example Array
*/
public function myFunction( $arr ){
if(!array_key_exists('mykey', $arr)){
echo 'Damnit, we needed an array that had mykey as an index!';
}
}
You could write a list of such keys, and place it in the #param description, or put it in the long description of the method's docblock. There is nothing "automated" or "rigid" that phpDocumentor could really do in the documentation here, e.g. link to some other documented element.
No, if you need a specific variable, make it a separate parameter. In that case, if the parameter is empty, an error is thrown.
You can't specify the data-type of array values in PHP. So there is noeed to document that via PHPDoc.
NO there is not. Best to just put it in the comments section.

Array of parameters

CakePHP makes heavy use of associative arrays for passing large numbers of parameters to functions. I have not really seen this technique outside of PHP and never seen it used to the extent that Cake uses it. I really like this approach because as it seems it would be easier to handle new parameters in future releases of your own code and it's alot more readable than simply a long list of params.
As an example...
function myFunc($params = array('name' => 'rob', 'count' => 5, 'anArray' => array('A string', 5, myObject)))
{
// ...
}
I guess this is similar to using argc/argv, but is a bit easier to read. Does anyone have a list of pros and cons on this method or know of anyone that's written on best practices with this? I've tried just Googling it but "associative array of parameters" brings up pretty much every programming article ever written.
Also, is there even a term for passing parameters this way?
A downside to using named parameters is documenting the parameters with PHPDoc. Many editors/IDEs provide "automatic" documentation that can parse your code and generate generic docblocks.
e.g.
function foo(array $bar, SomeClass $stuff) { returns $magic; }
would produce:
/**
* foo
*
* #param array $bar
* #param SomeClass $stuff
* #return mixed
*/
function foo(array $bar, SomeClass $stuff) { returns $magic; }
If you put all your parameters in a $params array, it would only look like
/**
* foo
*
* #param array $params
* #return mixed
*/
It also adds a lot of additional burden to your developers to have to type the extra code for each parameter. I'd suggest using a mix of the two approaches.
e.g. If you had a function to return an HTML text input element you could have the following method signature:
/**
* formText
*
* #param string $name name of text element
* #param string $value value of text element
* #param array $options additional options for text element
* #return string
*/
function formText($name, $value, $options = array());
Thus, you can easily pass the most common values to the function.
$this->formText('foo', 'Default...');
and if you need additional, less common params you'd use the convenient named parameter syntax:
$this->formText('foo', 'Default...', array(
'class' => 'bold highlighted'
));
otherwise, using only a generic $params array you'd have to type:
$this->formText(array(
'name' => 'foo',
'value' => 'Default...'
));
It is a emulation of using keyword arguments. In python, for example, you can say:
myFunc(name="bob", age=10, gender="male")
Since PHP does not support this syntax, associative arrays are the next-best-thing.
Other high-level languages support named parameters to functions and methods. For instance, in Python you can call:
my_func(name='rob', count=5, an_array=['A string', 5, my_object])
What you are seeing is an attempt to simulate this behavior in PHP. The benefits are obvious.
Flexibility
No need to know order/number of expected parameters
One drawbacks might be that a hash table lookup is required for every method call, but depending on how arguments are handled in PHP, the performance hit may be negligible.
It's the only way to do named parameters in PHP.
It's only useful when you have a large number of arguments, IMO. Otherwise the benefits of an explicitly defined argument signature are the better choice, especially if you use an "intellisense" capable IDE.
This technique is (really) often used in Javascript, with objects (when you are using string as keys, in JS, you are using objects, not arrays) ; for an example, see scriptaculous Draggable.
Now, for a couple of pros and cons that immediatly come to mind :
pro : you can use any number of parameters you want, and still have them named
and there is no "only parameters at the end of the list are optionnal"
con : phpdoc is not fine : it seems there is only one parameter, and there is not much indication on what it does
con (consequence of the previous one) : when you are using an IDE with code-hints, it cannot display names/description for each parameter : you always have to check the documentation.
That single "con" is enough for me : I only use that way of passing parameters if there is no other (reallistic) way.

Categories