Adding null values to an array - php

I have the this method:
public function search($searchKey=null, $summary=null, $title=null, $authors=null, $paginationPage=0) {
...
}
And I'm trying to retrieve all parameters with this:
$Class = new Search();
// Get parameters
$ReflectionMethod = new \ReflectionMethod($Class, "search");
try {
foreach($ReflectionMethod->getParameters() AS $Parameter) {
if(array_key_exists($Parameter->name, $this->params)) {
$parameters[$Parameter->name] = $this->params[$Parameter->name];
} elseif($Parameter->isDefaultValueAvailable()) {
$paramaters[$Parameter->name] = $Parameter->getDefaultValue();
} else {
...
}
} catch(\Exception $e) {
...
}
// Call function
return call_user_func_array(array($Class, "search"), $parameters);
My $this->params has this content:
array
'paginationPage' => int 2
'id' => int 30
'searchKey' => string 'test' (length=4)
Because $summary, $title and $authors are not present, they will get their default value which is null. When assigning a null value to an argument, it will be skipped which results in a $parameters array that looks like this:
array
'searchKey' => string 'test' (length=4)
'paginationPage' => int 2
Which result in a method call like:
public function search('test', 2, null, null, 0) {
...
}
While it should be:
public function search('test', null, null, null, 2) {
...
}
Hopefully you see the problem. How can I make sure those null values are also put into my $parameters array. Adding an invalid value is not possible, because it is user input, so that can be basically everything.
Edit
In the example above the method search is hardcoded. But one of the simplified things is that search is actually a variable and because of that search can be anything. This means that I don't know what the parameters of the method are and I can't predefine them before the foreach loop. The solution of predefining the parameters is actually exactly what this code should be doing.

How about pre-initializing $parameters before entering the foreach loop:
$parameters = array(
$searchKey => null,
$summary => null,
$title => null,
$authors => null,
$paginationPage => 0
);

Oh my... It was just a simple typo:
...
} elseif($Parameter->isDefaultValueAvailable()) {
$paramaters[$Parameter->name] = $Parameter->getDefaultValue();
} else {
...
Shame on me!

Related

How to return an element of array, which finded by recursion?

So, i have a little trouble:
I have a class, let's name it "Menu", also, i have an array, which provides an elements for "Menu", it looks like that
class Menu {
private $_data = [];
public function __construct() {
$this->_data = array(
"Parent1" => array(
"Child1" => array(
"id" => 1,
"minQuantity" => x,
"maxQuantity" => x,
"cost" => x,
),
"Child2"...
),
"ParentX" => array(
"ChildXX"...
)
/* AND SO ON */
);
}
}
Also, in "Menu" i have a function, which by recursion try to find an element of the $this->_data with specified value, function looks like that:
public function findChildById($parent = null, $id = null) {
foreach ($parent as $_parent => $_child) {
if (array_key_exists($id, $parent)) var_dump($parent);
if (is_array($_child)) $this->findChildById($_child, $id);
}
}
But, when it finds needed element, and I try to return it - result is always NULL. Using var_dump leads to obvious output, I can see what exactly what I need, but i cant return an element from function. What should i do?
Since you only try to find one element, it should be enough to pass the return value up the recursion stack. E.g. like this:
public function findChildById($parent = null, $id = null) {
foreach ($parent as $_parent => $_child) {
if (array_key_exists($id, $parent)) return $parent; //first return
if (is_array($_child)) {
$tmp = $this->findChildById($_child, $id);
if (!is_null($tmp)) return $tmp; //if a deeper step found sth. pass it up
}
}
}
The reason for the NULLs you get, must be, because PHP functions return NULL implicitly when the code does not reach a return statement.

How can I initialize an array's argument to its default value?

Here is my code:
function myfunc( $arg1, $arg2 = 'sth', $arg3 = 'sth else' ){
// do stuff
}
Now I want to set a new value for arg3 and keep the default defined value for arg2. How can I do that?
Something like this:
myfunc( true, <keep everything defined as default>, $arg3 = 'new value' );
The expected result is sth in this fiddle.
A possible alternative would not have your function take 3 parameters, but only one, an array:
function my_function(array $value = array()) {
// if set, use $value['key1']
// if set, use $value['key2']
// ...
}
And call that function like this:
my_function(array(
'key1' => 'google',
'key2' => 'yahoo'
));
This would allow you to:
accept any number of parameters
all of which could be optional
Hope it will helpful.

Function returns string on first call, and an object on subsequent calls

I'm a little bit baffled by this, so I'm hoping someone can shed some light on it for me.
I have a function which is meant to return a column Status from one of my tables, visit.
function someFunction($visitId = null) {
$visit = VisitQuery::create()
->select(array('Status'))
->findPk($visitId);
}
If I var_dump($visit), when calling the function for the first time, it outputs:
string '1' (length=1)
Subsequent, identical calls to the function however seem to return an entire object:
object(Visit)[30]
protected 'startCopy' => boolean false
protected 'id' => int 362
protected 'job_id' => int 351
protected 'company_id' => int 2
protected 'type_id' => int 1
protected 'visit_date' => string '2013-08-23 00:00:00' (length=19)
protected 'status' => string '1' (length=1)
...
I'm calling the function for the first time with an (int) $visitId passed via a posted form:
var_dump($visitId); // int 362
Subsequent calls are made with an (int) $visitId which is returned from another function, saveVisit() (which uses Propel to save the record - I believe this may have something to do with it).
$visitId = saveVisit($visitId);
var_dump($visitId); // int 362
I tried to do some debugging, and for some reason the query issued to MySQL is different between the first function call and subsequent ones:
var_dump($con->getLastExecutedQuery());
SELECT visit.STATUS AS "Status" FROM `visit` WHERE visit.ID=362 // 1st call
SELECT `ID`, `JOB_ID`, `COMPANY_ID`, `TYPE_ID`, `VISIT_DATE`, `STATUS`, `REMIND`, `PRINTED` FROM `visit` WHERE `ID` = 362 // 2nd call
SELECT `ID`, `JOB_ID`, `COMPANY_ID`, `TYPE_ID`, `VISIT_DATE`, `STATUS`, `REMIND`, `PRINTED` FROM `visit` WHERE `ID` = 362 // 3rd call
Can anyone tell me why or how this is happening?
I'm using Propel 1.6.
Propel's create() method:
public static function create($modelAlias = null, $criteria = null)
{
if ($criteria instanceof VisitQuery) {
return $criteria;
}
$query = new VisitQuery();
if (null !== $modelAlias) {
$query->setModelAlias($modelAlias);
}
if ($criteria instanceof Criteria) {
$query->mergeWith($criteria);
}
return $query;
}
Propel's findPk() method:
public function findPk($key, $con = null)
{
if ($con === null) {
$con = Propel::getConnection($this->getDbName(), Propel::CONNECTION_READ);
}
// As the query uses a PK condition, no limit(1) is necessary.
$this->basePreSelect($con);
$criteria = $this->isKeepQuery() ? clone $this : $this;
$pkCols = $this->getTableMap()->getPrimaryKeyColumns();
if (count($pkCols) == 1) {
// simple primary key
$pkCol = $pkCols[0];
$criteria->add($pkCol->getFullyQualifiedName(), $key);
} else {
// composite primary key
foreach ($pkCols as $pkCol) {
$keyPart = array_shift($key);
$criteria->add($pkCol->getFullyQualifiedName(), $keyPart);
}
}
$stmt = $criteria->doSelect($con);
return $criteria->getFormatter()->init($criteria)->formatOne($stmt);
}
My function to retrieve the visit status:
function getVisitStatus($visitId = null) {
if (empty($visitId)) {
return false;
}
try {
$visit = VisitQuery::create()
->select(array('Status'))
->findPk($visitId);
} catch (Exception $e) {
echo $e->getMessage(); exit;
}
if (is_null($visit)) {
return false;
}
return $visit;
}
The function which saves a visit record:
function saveVisit($data = null) {
if (empty($data)) {
return false;
}
try {
$visit = (!empty($data['visit_id'])) ? VisitQuery::create()->findPk($data['visit_id']) : new Visit();
if (!is_object($visit)) {
return false;
}
$visitDataMap = array(
'JobId' => 'job_id',
'TypeId' => 'type_id',
'VisitDate' => 'visit_date',
'Status' => 'status',
'CompanyId' => 'engineer_id',
'Remind' => 'remind'
);
$visitData = array();
foreach ($visitDataMap as $column => $value) {
if (!empty($data[$value])) {
$visitData[$column] = $data[$value];
}
}
$visit->fromArray($visitData);
$visit->save();
return $visit->getId();
} catch (PropelException $e) {
echo $e->getMessage(); exit;
}
}
It seems that on the first call will grab your data but then put a copy of the full object in to the instance pool. I am not sure if this is a bug or valid behaviour (your code would suggest the former, but I'd love to hear from someone who knows more about Propel such as a dev) but you can stop it happening with:
VisitPeer::clearInstancePool();
Bear in mind you're losing a bit of caching from other queries you might have done here with the visit relation.
You will be able to confirm this by adding an echo in the BaseVisitPeer.php file. You will see something like this:
public static function getInstanceFromPool($key)
{
if (Propel::isInstancePoolingEnabled()) {
if (isset(VisitPeer::$instances[$key])) {
return VisitPeer::$instances[$key];
}
}
return null; // just to be explicit
}
If you add an echo somewhere inside the if (isset(VisitPeer::$instances[$key])) { statement, you should see it appear only after the second and subsequent calls. If you were to comment this whole if statement out then you would get the same result back each time - the one you correctly get back from your original call.
Hope that helps!

PHP: name parameters when passing them to a method?

Some languages allow us to name parameters when passing them to methods, e.g.:
private static function hello(a, long, list = 0, of = 0, parameters = 0)
{
...
}
self::hello(a=1, long=2, list=3, of=4);
It certainly simplifies reading code in some cases. Is there a similar functionality in php?
The most simple construction that I could imagine is:
self::hello(/*a*/ 1, /*long*/ 2, /*list*/ 3, /*of*/ 4);
[offtopic]Love Obj C... It requires to name parameters:[self helloA:1 long:2 list:3 of:4];[/offtopic]
PHP does not provide such functionality but it can be faked to a large degree of realism. I just wrote this thing. Not 100% tested but if used correctly, it will work.
Example of how you use it:
call_user_func_args(function($a1, $a2, array $a3 = null, $a4 = null){
var_dump(func_get_args());
}, array(
'a1' => 1,
'a3' => null,
'a2' => 2,
));
// Output
array (size=3)
0 => int 1
1 => int 2
2 => null
It uses \ in front of classes as it's namespaced code intended for use with PHP 5.3 or later. Remove the \ to try to use it in 5.2, but no guarantees.
Here is the code:
/**
* Calls a function with named arguments.
* Just written and quite tested. If you find bugs, please provide feedback and I'll update the code.
* In a sane usage scenario, it will work. If you try your best, you might break it :)
* If true, $ValidateInput tries to warn you of issues with your Arguments, bad types, nulls where they should not be.
*
* #copyright Claudrian
* #param callable $Callable
* #param array $Arguments
* #param bool $ValidateInput
* #return mixed
*/
function call_user_func_args($Callable, array $Arguments, $ValidateInput = false){
// Make sure the $Callable is callable
if(!is_callable($Callable)){
trigger_error('$Callable is not a callable.', E_USER_WARNING);
return false;
}
// No arguments, no game
if(empty($Arguments)){
return call_user_func($Callable);
}
// Validate the input $Arguments
array_change_key_case($Arguments, CASE_LOWER);
foreach($Arguments as $ArgumentName => $ArgumentValue){
if(empty($ArgumentName) or is_numeric($ArgumentName)){
trigger_error('$Arguments cannot have numeric offsets.', E_USER_WARNING);
return false;
}
if(!preg_match('~^[a-z_][a-z0-9_]*$~', $ArgumentName)){
trigger_error('$Arguments contains illegal character offsets.', E_USER_WARNING);
return false;
}
}
// Get access to the function
try {
$Reflector = new \ReflectionFunction($Callable);
} catch(\Exception $Exception){
trigger_error($Exception->getMessage(), E_USER_WARNING);
return false;
}
// If function has not arguments, just call it but it's stupid
$RequiredParameterCount = $Reflector->getNumberOfRequiredParameters();
$ParameterCount = $Reflector->getNumberOfParameters();
if(!$ParameterCount){
return call_user_func($Callable);
}
// Prepare the $Parameters
$Parameters = array();
$PresetParameters = array();
foreach($Reflector->getParameters() as $Parameter){
$LowerName = strtolower($Name = $Parameter->getName());
$Argument = ($Available = array_key_exists($Name, $Arguments)) ? $Arguments[$Name] : null;
$Default = ($IsDefault = $Parameter->isDefaultValueAvailable()) ? $Parameter->getDefaultValue() : null;
$Parameters[$LowerName] = array(
'Name' => $Name,
'Offset' => $Parameter->getPosition(),
'Optional' => $Parameter->isOptional(),
'Nullable' => $Parameter->allowsNull(),
'Reference' => $Parameter->isPassedByReference(),
'Array' => $Parameter->isArray(),
'Defaultable' => $IsDefault,
'Default' => $Default,
'Available' => $Available,
'Provided' => $Available ? $Argument : $Default,
);
}
// Pop pointless nulls (from the last to the first)
end($Parameters);
while($Parameter = current($Parameters)){
if(!$Parameter['Nullable'] or !$Parameter['Optional'] or !is_null($Parameter['Provided'])){
break;
}
array_pop($Parameters); // Pop trailing null optional nullable arguments
prev($Parameters); // Move one back
}
// Prepare the final $Arguments
$Arguments = array();
foreach($Parameters as $Name => $Parameter){
if($ValidateInput){
if(is_null($Parameter['Provided']) and !$Parameter['Nullable']){
trigger_error("Argument '{$Name}' does not accept NULL.", E_USER_NOTICE);
}
if($Parameter['Array'] and !is_array($Parameter['Provided'])){
if(!$Parameter['Nullable'] and is_null($Parameter['Provided'])){
trigger_error("Argument '{$Name}' should be an array.", E_USER_NOTICE);
}
}
if(!$Parameter['Available'] and !$Parameter['Optional'] and !$Parameter['Defaultable']){
trigger_error("Argument '{$Name}' is not optional and not provided.", E_USER_NOTICE);
}
}
// Stoe this in the final $Arguments array
$Arguments[] = $Parameter['Provided'];
}
// Invoke the actual function
return $Reflector->invokeArgs($Arguments);
}
You could do
$a=1;
$long=2;
$list=3;
$of=4;
self::hello($a, $long, $list, $of);
Other way, would be to use setters to set the values in object before You call hello.
Though in Your example it's a private method...
Your reference here should be http://www.php.net/manual/en/functions.arguments.php

What is the best way of reading parameters in functions?

I've created several helper functions which I use when creating templates for Wordpress.
An example is this:
function the_related_image_scaled($w="150", $h="150", $cropratio="1:1", $alt="", $key="related" )
The problem is, if I only want to pass along the $alt parameter, I also have to populate $w, $h and $cropratio.
In one of my plugins, I use the following code:
function shortcode_display_event($attr) {
extract(shortcode_atts(array(
'type' => 'simple',
'parent_id' => '',
'category' => 'Default',
'count' => '10',
'link' => ''
), $attr));
$ec->displayCalendarList($data);
}
This allows me to call the function only using e.g.count=30.
How can I achieve the same thing in my own functions?
SOLUTION
Thanks to my name brother (steven_desu), I have come up with a solution that works.
I added a extra function (which I found on the net) to create value - pair from a string.
The code looks as follows:
// This turns the string 'intro=mini, read_more=false' into a value - pair array
function pairstr2Arr ($str, $separator='=', $delim=',') {
$elems = explode($delim, $str);
foreach( $elems as $elem => $val ) {
$val = trim($val);
$nameVal[] = explode($separator, $val);
$arr[trim(strtolower($nameVal[$elem][0]))] = trim($nameVal[$elem][1]);
}
return $arr;
}
function some_name($attr) {
$attr = pairstr2Arr($attr);
$result = array_merge(array(
'intro' => 'main',
'num_words' => '20',
'read_more' => 'true',
'link_text' => __('Read more')
), $attr);
extract($result);
// $intro will no longer contain'main' but will contain 'mini'
echo $intro;
}
some_name('intro=mini, read_more=false')
Info
With good feedback from Pekka, I googled and found some info regarding the Named Arguments and why it's not in PHP: http://www.seoegghead.com/software/php-parameter-skipping-and-named-parameters.seo
I would suggest using array_merge() and extract() at the beginning of your function, then passing parameters as arrays if this is a possibility.
function whatever($new_values){
$result = array_merge(array(
"var1" => "value1",
"var2" => "value2",
"var3" => "value3"
), $new_values);
extract($result);
echo "$var1, $var2, $var3";
}
whatever(array("var2"=>"new_value"));
The above will output:
value1, new_value, value3
it's a bit sloppy and uses more memory since it has to allocate the arrays, so it's the less efficient solution. But it does allow you to avoid redundancy. I'm sure a better method exists using magic meta-code, but I can't think of it off-hand.
Say this is your function:
function related_image_scaled($w="150", $h="150", $alt="", $key="related")
You can do this:
class ImageScaleParams {
public $w = 150;
public $h = 150;
public $cropratio = "1:1";
public $alt = "";
public $key = "related";
}
function related_image_scaled(ImageScaleParams $params) { ... }
Then call it like this:
$imgscale = new ImageScaleParams();
$imgscale.alt="New Alt";
related_image_scaled($imgscale);
You can also have various factories in ImageScaleParams such as:
class ImageScaleParams {
static function altFactory($alt) {
$imgscale = new ImageScaleParams();
$imgscale->alt = $alt;
return $imgscale;
}
}
Which you could call like this (equivalent to previous example):
related_image_scaled(ImageScaleParams::altFactory("New Alt"));
New Answer:
Could you not write the function using the extract function's default EXTR_OVERWRITE option?
function the_related_image_scaled($params) {
$w="150"; $h="150"; $cropratio="1:1"; $alt=""; $key="related";
extract($params);
//Do Stuff
}
Called with:
the_Related_image_scaled(array("alt"=>"Alt Text"));
You have the option of defaulting the parameters to null and only using them if they are not null:
function the_related_image_scaled($w=null, $h=null, $cropratio=null, $alt=null, $key = null) {
$output = //Get the base of the image tag
//including src leave a trailing space and don't close
if($w!==null) {
$output .= "width=\"$w\"";
}
//... Through all your parameters
return $output;
}
So only passing the alt parameter would look like:
echo the_related_image_scaled(null,null,null,$alt);

Categories