how arguments are passed to codeigniter method - php

I have a method in a codeigniter controller which is sometimes called through the url and sometimes called internally from another method of the controller. When I call it internally I pass an array of arguments. Simplified version of method:
(within a controller)
function get_details($args='') {
if (isset($args['first_name']))
{
$first_name = $args['first_name'];
}
else
{
$first_name = $this->uri->segment(3);
}
... do some other stuff ...
}
The method is either called as:
<domain>/<controller>/get_details/abcd/efgh
or from another function of the controller as:
$this->get_details(array('first_name'=>'abcd', 'last_name'=>'efgh'));
I was expecting that when the method was called through the url, isset($args['first_name']) would be false, however it seems that called in this way the argument is there. I tried printing a couple of things and this is what I got:
print_r($args) ----> abcd
echo($args['first_name']) ----> a
echo($args['whatever_index_I_use']) ----> a
It seems like the third parameter of the url is being passed into the method (by codeigniter?), but can't work out why the array indexes seem to be set, all I can think is that php is converting the string to an int, so $args['whatever_index_I_use'], becomes $args[0]??
Not sure if this is a codeigniter thing or me missing a subtlety of php.
Much appreciate anyone who can explain what's going on.
Thanks.

I don't know if this is a bug or a expected behavior, but in the Strings docs there's a comment that show exactly what are you experiencing. If you use a text and index of the string it will return the first char. To avoid it, check first if the argument is an array or a string:
if(is_array($args)) {
echo($args['first_name']);
}

To complete #SérgioMichels answer, the reason for that is because PHP is expecting an integer as the given index. When you give it a string, PHP will cast the string into an integer, and assuming that the string does not start with a number, type casting will return 0 otherwise, it will return the leading number.
$str = 'abcdefghi';
var_dump($str['no_number']); // Outputs: string(1) "a"
var_dump($str['3something']); // Outputs: string(1) "d"

To specifically answer your question - this will solve your bug:
function get_details($args='')
{
if (is_array($args))
{
$first_name = $args['first_name'];
}
else
{
$first_name = $this->uri->segment(3);
}
... do some other stuff ...
}
But you have some issues with your code. Firstly you state that you call the method as
<domain>/<controller>/get_details/abcd/efgh
but you dont accept the "efgh" variable in your controller. To do this, you need to change the function to
function get_details($first, $last)
in which case you can now just call the function as
$this->get_details('abcd', 'efgh');
and now you dont even need to test for arrays etc, which is a better solution IMO.
If you decide to stick with arrays, change:
$first_name = $this->uri->segment(3);
to
$first_name = $args;
because by definition - $args IS The 3rd URI segment.

Related

Function with two outputs

First, let me start by saying i'm a real beginner learning mostly PHP. Understanding the patterns and semantics of programming is more important to me than just learning the syntax. I did some research for what I'm going to ask, but I couldn't find any info about it. So, I was thinking... What if I need to do the following... Give a function multiple outputs to pass in other parts of the code. What is the name of this type of functionality (if it exists)? Or If it doesn't exist, why not? And what is the best practice to achieve the same semantic?
More details:
Let's say I want to call a function with one argument and return not only the value back to that same argument call location, but also in another location or into some other part of the program. Meaning a function with two outputs in two different locations, but the second location wouldn't be a call, just an output from the call made in the first location, returned at the same time with the same value output as the first. So, not calling it twice separately... But rather calling once and outputting twice in different locations. The second output would be used to pass the result into another part of the program and therefore wouldn't be appropriate as a "call/input", but more as an "output" only from the "input" value. How can I achieve multiple outputs in functions?
If this seems like a stupid question, I'm sorry. I couldn't find the info anywhere. Thanks in advance
What you want to do is basically this (i'll make it a 'practical' example):
function add($number1, $number2)
{
$result = $number1 + $number2;
return array('number1' => $number1,'number2' => $number2,'result' => $result);
}
$add = add(5,6); // Returns array('number1' => 5, 'number2' => 6, 'result' => 11);
You now have the two arguments and the result of that function at your disposal to use in other functions.
some_function1($add['result']);
...
some_function2($add['number1']);
If the question is about returning more than one variables, it is simply:
function wtf($foobar = true) {
$var1 = "first";
$var2 = "second";
if($foobar === true) {
return $var2;
}
return $var1;
}
You can either have the function return an array of values:
function foo($bar) {
$bat = 1;
return [$bar, $bat];
}
Or you can pass an argument that tells it which value to return:
function foo($bar, $return_bar=false) {
$bat = 1;
return $return_bar ? $bar : $bat;
}

PHP store function in array

i want to store function in array
send the array to another page
then execute it
i already read Can you store a function in a PHP array but still don't know what to do
here's what i try
control.php (it start here)
<?php
function getFirstFunction(){ echo "first function executed"; }
$data = array();
$data[0] = getFirstFunction();
$data[1] = function(){ echo "second function executed"; };
$data[2] = function(){ require_once "additional.php"; };
$data[3] = "my string";
header('Location: view.php?data='.$data);
?>
additional.php
<?php echo "additional included" ?>
view.php
<?php
if( isset($_GET['data']) ){
foreach( $_GET['data'] as $temp ){
if( is_callable($temp) ){
$temp;
}else{
"its not a function";
}
}
}
?>
my error =
Warning: Invalid argument supplied for foreach() in D:\Workspace\Web\latihanns\php\get\view.php on line 4
EDIT
thanks for notify that this code its dangerous.i'm not use this code in real live. i just try to learn store function in array then call it. then i just curious how if i call it on another page. i just simply curious... i make my code look clear and simple here because i afraid if i wrote complicated code, no one will be here or my post will closed as too localized...
If you want to pass anything than string into URL, only option is convert it to string form which is reversible to original types. PHP offers function called serialize() which converts anything to string. After that, you can call unserialize() to convert string back to original data. So you have to change one line in control.php to this:
header('Location: view.php?data='.serialize($data));
In file view.php you have to change one line to this:
foreach( unserialize($_GET['data']) as $temp ){
But you have to fix more things than this. If you have callable variable, you can't invoke function with $variable, but with $variable(). It is good to mention, that in PHP does not matter if you have real function (anonymous function, Closure etc.) in variable, or if variable is simple string with name of exists function.
However you have even another bug in control.php. Code $data[0] = getFirstFunction(); will not pass function getFirstFunction an make it callable, it just calls the function and put its return value to variable. You can define getFirstFunction as anonymouse function like function in $data[1] or just pass it as string like $data[0] = 'getFirstFunction' which will work.
At the end - as anyone mentioned here - IT IS VERY DANGEROUS ans you shouldn't use this on public server.

Why doesn't my code to test an email address against a specific domain work?

I wanted to allow only specific email domain. Actually I did it. What i wanted to ask why my first code did not work at all.
I am just trying to learn PHP so that the question may seem silly, sorry for that.
Here is my code:
function check_email_address($email) {
$checkmail = print_r (explode("#",$email));
$container = $checkmail[1];
if(strcmp($container, "gmail.com")) {
return true;
}else {
return false;
}
}
Check out the documentation for strcmp() , it will return 0 of the two strings are the same, so that's the check you want to be doing. Also, you're using print_r() when you shouldn't be, as mentioned by the other answerers.
Anyway, here's how I would have done the function - it's much simpler and uses only one line of code:
function check_email_address($email) {
return (strtolower(strstr($email, '#')) == 'gmail.com');
}
It uses the strstr() function and the strtolower() function to get the domain name and change it to lower case, and then it checks if it is gmail.com or not. It then returns the result of that comparison.
It's because you're using print_r. It doesn't do what you seem to expect from it at all. Remove it:
$checkmail = explode("#", $email);
You can find the docs about print_r here:
http://php.net/print_r
Besides that, you can just use the following (it's much shorter):
$parts = explode("#", $email);
return (strcmp($parts[1], "gmail.com") == 0);
The following row doesn't work as you think it does:
$checkmail = print_r (explode("#",$email));
This means that you're trying to assign the return value from print_r() into $checkmail, but it doesn't actually return anything (if you don't supply the second, optional parameter with the value true).
Even then, it would've gotten a string containing the array structure, and your $container would have taken the value r, as it's the second letter in Array.
Bottom line: if your row would've been without the call to print_r(), it would've been working as planned (as long as you made sure to compare the strcmp() versus 0, as it means that the strings are identical).
Edit:
Interesting enough, I just realized that this could be achieved with the use of substr() too:
<?php
//Did we find #gmail.com at the end?
if( strtolower(substr($email, -10)) == '#gmail.com' ) {
//Do something since it's an gmail.com-address
} else {
//Error handling here
}
?>
You want:
if(strcmp($container, "gmail.com")==0)
instead of
if(strcmp($container, "gmail.com"))
Oh! And no inlined print_r() of course.
Even better:
return strcmp($container, "gmail.com")==0;
No need for the print_r; explode returns a list. And in terms of style (at least, my style) no need to assign the Nth element of that list to another variable unless you intend to use it a lot elsewhere. Thus,
$c = explode('#',$mail);
if(strcmp($c[1],'gmail.com') == 0) return true;
return false;

How to Pass Class Variables in a Function Parameter

The main function of the example class uses the reusableFunction twice with different data and attempts to send that data to a different instance variable ($this->result1container and $this->result2container) in each case, but the data doesn't get into the instance variables.
I could get it to work by making reusableFunction into two different functions, one with array_push($this->result1container, $resultdata) and the other with array_push($this->result2container, $resultdata), but I am trying to find a solution that doesn't require me to duplicate the code.
My solution was to try to pass the name of the result container into the function, but no go. Does somebody know a way I could get this to work?
Example Code:
Class Example {
private $result1container = array();
private $result2container = array();
function __construct() {
;
}
function main($data1, $data2) {
$this->reusableFunction($data1, $this->result1container);
$this->reusableFunction($data2, $this->result2container);
}
function reusableFunction($data, $resultcontainer) {
$resultdata = $data + 17;
// PROBLEM HERE - $resultcontainer is apparently not equal to
// $this->result1container or $this->result2container when I
// try to pass them in through the parameter.
array_push($resultcontainer, $resultdata);
}
function getResults() {
return array(
"Container 1" => $this->result1container,
"Container 2" => $this->result2container);
}
}
(If this is a duplicate of a question, I apologize and will happily learn the answer from that question if somebody would be kind enough to point me there. My research didn't turn up any answers, but this might just be because I didn't know the right question to be searching for)
It looks to me like you want to be passing by reference:
function reusableFunction($data, &$resultcontainer) {
...
If you don't pass by reference with the & then you are just making a local copy of the variable inside reuseableFunction .
You are changing the copy, not the original. Alias the original Array by referenceDocs:
function reusableFunction($data, &$resultcontainer) {
# ^
And that should do the job. Alternatively, return the changed Array and assign it to the object member it belongs to (as for re-useability and to keep things apart if the real functionality is doing merely the push only).
Additionally
array_push($resultcontainer, $resultdata);
can be written as
$resultcontainer[] = $resultdata;
But that's just really FYI.
You may pass the attributes name as a String to the method like this:
function reusableFunction($data, $resultcontainer) {
$resultdata = $data + 17;
array_push($this->{$resultcontainer}, $resultdata);
}
//..somewhere else..
$this->reusableFunction($data, 'result2Container')
Some php experts wrote some texts about "why you shouldn't use byReference in php".
Another solution would be to define the containers as an array. Then you can pass an "key" to the method that is used to store the result in the array. Like this:
private $results = array();
function reusableFunction($data, $resIdx) {
$resultdata = $data + 17;
array_push($this->$results[$resIdx], $resultdata);
}
//..somewhere else..
$this->reusableFunction($data, 'result2Container');
//..or pass a number as index..
$this->reusableFunction($data, 1);

PHP how to use string as superglobal

I'm building a small abstract class that's supposed to make certain tasks easier.
For example:
$var = class::get('id');
would run check if there's pointer id in the $_GET, returning a string or array according to parameters. This should also work for post and request and maby more.
I'm doing it in the way there's function for all the superglobals. I'm using get as example:
get function gets a pointer as parameter, it calls fetchdata function and uses the pointer and "$_GET" as the parameters.
fetchdata is supposed to just blindly use the string it got as superglobal and point to it with the other param. Then check if it exists there and return either the value or false to get function, that returns the value/false to caller.
Only problem is to get the string work as superglobal when you don't know what it is. I did this before with a switch that checked the param and in case it was "get", it set $_GET to value of another variable. However I don't want to do it like that, I want it to be easy to add more functions without having to touch the fetchdata.
I tried $method = eval($method), but it didn't work. ($method = "$_GET"), any suggestions?
EDIT: Sorry if I didn't put it clear enough. I have a variable X with string value "$_GET", how can I make it so X gets values from the source described in the string?
So simply it's
$X = $_GET if X has value "$_GET"
$X = $_POST if X has value "$_POST"
I just don't know what value X has, but it needs to get data from superglobal with the same name than its value.
According to this page in the manual:
Note: Variable variables
Superglobals cannot be used as variable variables inside functions or class methods.
This means you can't do this inside a function or method (which you would be able to do with other variables) :
$var = '_GET';
${$var}[$key]
Instead of passing a string to fetchdata(), could you not pass $_GET itself? I think PHP will not copy a variable unless you modify it ('copy on write'), so this shouldn't use memory unnecessarily.
Otherwise there are only nine superglobals, so a switch-case as you have suggested isn't unreasonable.
You could do this with eval() if you really had to, something like:
eval('return $_GET;');
I think that would be unnecessary and a bad idea though; it is slow and you need to be extremely careful about letting untrusted strings anywhere near it.
Don't use eval. Just use reference.
//test value for cli
$_GET['test'] = 'test';
/**
* #link http://php.net/manual/en/filter.constants.php reuse the filter constants
*/
function superglobalValue($key, $input = null) {
if ($input === INPUT_POST)
$X = &$_POST;
else
$X = &$_GET;
return (isset($X[$key]) ? $X[$key] : false);
}
function getArrayValue(&$array, $key) {
return (array_key_exists($key, $array) ? $array[$key] : false);
}
//test dump
var_dump(
superglobalValue('test', INPUT_GET),
superglobalValue('test', INPUT_POST),
getArrayValue($_GET, 'test'),
getArrayValue($_POST, 'test')
);
$_GET, $_POST and $_REQUEST dont have any null values by default, only string or array. So I used isset there instead of array_key_exists.
Param order: I always put required params before optional when I can, and the data objects before the manipulation/subjective params. Thats why key is first param for superglobalValue and second param for getArrayValue.
I'm not quite sure what you're trying to achieve, but you could have a look at the __callStatic magic method
class example{
protected static $supers = array('GET', 'POST', 'SERVER', 'COOKIE');
public static function __callStatic($functionName, $arguments){
$index = arguments[0];
$desiredSuper = strtoupper($functionName);
if(in_array($desiredSuper, self::$supers)){
doStuff ( $_{$desiredSuper}[$index] );
}
else{
throw new Exception("$desiredSupper is not an allowed superGlobal");
}
}
}
you could then do:
example::get('id'); //wo do stuff to $_GET['id']
example::server('REQUEST_METHOD'); //Will do stuff to $_SERVER['REQUEST_METHOD']
example::foo('bar'); //throws Exception: 'FOO is not an allowed superGlobal'
Php manual on magic methods: http://ca.php.net/manual/en/language.oop5.overloading.php#language.oop5.overloading.methods
Edit
I just noticed your edit, you could try:
$X = {$X};
You can use $_REQUEST["var"] instead of $_GET["var"] or $_POST["var"].
A more complicated way would be to test if the variable exists in the GET array, if it doesnt then its POST. If it does its GET.
$var = null;
if (isset($_GET["varname"]))
{
$var = $_GET["varname"];
}
else
{
$var = $_POST["varname"];
}
If you want a variable to be accessible globally, you can add it tot he $GLOBALS array.
$GLOBALS['test']='test';
Now you can fetch $GLOBALS['test'] anywhere.

Categories