I find in my PHP pages I end up with lines and lines of code that look like this:
$my_id = isset($_REQUEST['my_id']) ? $_REQUEST['my_id'] : '';
$another_var = isset($_REQUEST['another_var']) ? $_REQUEST['another_var'] : 42;
...
Is there a better, more concise, or more readable way to check this array and assign them to a local variable if they exist or apply a default if they don't?
EDIT: I don't want to use register_globals() - I'd still have the isset problem anyway.
How about wrapping it in a function?
<?php
function getPost($name, $default = null) {
return isset($_POST[$name]) ? $_POST[$name] : $default;
}
a better method might be to create a singleton/static class to abstract away the details of checking the request data.
Something like:
class Request {
private $defaults = array();
private static $_instance = false;
function getInstance () {
if (!self::$_instance) {
$c = __CLASS__;
self::$_instance = new $c;
}
return self::$_instance;
}
function setDefaults($defaults) {
$this->defaults = $defaults;
}
public function __get($field) {
if (isset($_REQUEST[$field]) && !empty($_REQUEST[$field])) {
return $_REQUEST['field'];
} elseif (isset($this->defaults[$field])) {
return $this->defaults[$field];
} else {
return ''; # define a default value here.
}
}
}
you can then do:
# get an instance of the request
$request = Request::getInstance();
# pass in defaults.
$request->setDefaults(array('name'=>'Please Specify'));
# access properties
echo $request->name;
echo $request->email;
I think this makes your individual scripts loads cleaner and abstracts away the validation etc. Plus loads of scope with this design to extend it/add alternate behaviours, add more complicated default handling etc etc.
First, use $_POST for POSTed variables. $_REQUEST is a mashup of many different incoming variables, not just $_POST and could cause problems.
One solution for your question would be to create a function that handles the isset() logic.
function ForceIncomingValue($Key, $Default) {
if (!isset($_POST[$Key]))
return $Default;
else return $_POST[$Key];
}
first of all, NEVER use the $_REQUEST variable, it'll lead to bugs and other problems during development
function getPOST($key) {
if(isset($_POST[$key])) {
return $_POST[$key];
}
}
note that this code leaves the variable empty when $_POST[$key] was not set
you could also adapt that code to enable you to instead provide you with a (sensible) default when the value could not be loaded.
function getPOST($key, $default = NULL) {
if(isset($_POST[$key])) {
return $_POST[$key];
} else {
return $default;
}
}
Is the set of variables you're expecting known at the time of the script's writing, or do you want to do this for an arbitrary set of values? If the former is true, you could do something like this:
# This array would hold the names of all the variables you're expecting
# and a default value for that variable name
$variableNames = array (...);
foreach ($variableNames as $key => $default) {
if (isset ($_REQUEST[$key])) $$key = $_REQUEST[$key];
else $$key = $default;
}
Basically, this takes advantage of PHP's ability to evaluate variables to create other variables (hence the double-dollar for $$key--this means create a new variable whose name is the value of $key).
I haven't yet come up with a good solution to the latter situation.
PHP's null coalescing operator!
$username = $_GET['user'] ?? 'nobody';
For a lot of variables, with a requirement check, anyone is free to use my expect function.
Related
I have a bunch of optional settings and I'm sick of checking for isset and property_exists.
In Laravel, if I ask for a property that does not exist on a model or request, I get null and no complaints (errors). How can I do the same for my data structure.
If I try array, I can't do simple $settings['setting13'], I have to either pre-fill it all with nulls or do isset($settings['setting13']) ? $settings['setting13'] : '' or $settings['setting13'] ?? null. If I try an object (new \stdClass()), $settings->setting13 still gives me a warning of Undefined property.
How can I make a class such that it responds null or an empty string whenever it is asked for a property that it doesn't have?
Simply do what Laravel does, create a class that deals with your data structure which returns a value if key exists, and something else if it doesn't.
I'll illustrate with an example class (this class supports the "dot notation" of accessing array keys):
class MyConfigClass
{
protected $data;
public function __construct(array $data)
{
$this->data = $data;
}
public function get($path = '', $default = null)
{
if(!is_string($path))
{
return $default;
}
// There's a dot in the path, traverse the array
if(false !== strpos('.', $path))
{
// Find the segments delimited by dot
$segments = explode('.', $path);
$result = $this->data;
foreach($segments as $segment)
{
if(isset($result[$segment]))
{
// We have the segment
$result = $result[$segment];
}
else
{
// The segment isn't there, return default value
return $default;
}
}
return $result;
}
// The above didn't yield a result, check if the key exists in the array and if not - return default
return isset($this->data[$path]) ? $this->data[$path] : $default;
}
}
Use:
$my_structure = [
'url' => 'www.stackoverflow.com',
'questions' => [
'title' => 'this is test title'
]
];
$config = new MyConfigClass($my_structure);
echo $config->get('url'); // echoes www.stackoverflow.com
echo $config->get('questions.title'); // echoes this is test title
echo $config->get('bad key that is not there'); // returns null
There is also a possibility to create wrapper as Jon Stirling mentioned in a comments. This approach will allow to keep code clean and also add functionality via inheritance.
<?php
class myArray implements ArrayAccess {
private $container;
function __construct($myArray){
$this->container = $myArray;
}
public function offsetSet($offset, $value) {
if (is_null($offset)) {
$this->container[] = $value;
} else {
$this->container[$offset] = $value;
}
}
public function offsetExists($offset) {
return isset($this->container[$offset]);
}
public function offsetUnset($offset) {
unset($this->container[$offset]);
}
public function offsetGet($offset) {
return isset($this->container[$offset]) ? $this->container[$offset] : null;
}
}
$settings = array("setting1"=>1,"setting2"=>2,"setting3"=>3);
$arr = new myArray($settings);
echo $arr['setting1'];
echo "<br>";
echo $arr['setting3'];
echo "<br>";
echo $arr['setting2'];
echo "<br>";
echo "------";
echo "<br>";
echo $arr['setting4'] ?:"Value is null";
!empty($settings['setting13']) ? $settings['setting13'] : ''
can be replaced with
$settings['setting13'] ?: ''
as long as whatever you want to print and whatever you want to check exists is the same expression. It's not the cleanest thing ever - which would be to check the existence of anything - but it's reasonably clear and can be chained :
echo ($a ?: $b ?: $c ? $default ?: '');
However, you are not the first who are "sick of checking for isset and property_exists, it's just that we still have to do it, or else we get unexpected results when we expect it the least.
It's not about saving time typing code, it's about saving time not debugging.
EDIT : As pointed in the comments, I wrote the first line with isset() instead of !empty(). Since ?: returns the left operand if it's equal to true, it's of course uncompatible with unchecked variables, you have at least to check for existence beforehand. It's emptiness that can be tested.
The operator that returns its left operand if it exists and is different from NULL is ??, which can be chained the same way ?: does.
Admittedly not the best way to do this, but you can use the error suppressor in php like this:
$value = #$settings['setting13'];
This will quitely set$value to NULL if $settings['setting13'] is not set and not report the undefined variable notice.
As for objects, you should just calling for attributes that are not defined in class.
I think the below is a very standard procedure that everyone had written it a hundred times in any of their applications:
$orderByColumnName = '';
if (isset($this->urlParams()['orderby']) {
$orderByColumnName = $this->urlParams()['orderby'];
}
$this->someSortingFunction($orderByColumnName);
I've been doing such way numerous time, I'm feeling sick of it.
There's some kind of like global understanding that suppressing error is evil.
Despite the code can be written so clean by just:
$this->someSortingFunction(#$this->urlParams()['orderby']);
And a ternary shorthand true ?: false; is something close but not useful in this situation because there's no presumed isset check on the condition. Thus we still have to write:
$orderByColumnName = !empty($this->urlParams()['orderby'])?$this->urlParams()['orderby']:'';
So how exactly you guys handle this situation? if all the way? Is there any other smarter way that you could share?
Why not change how the urlParams() method operates? PHP doesn't have true method overloading, but you can fake it inside the method.
public function urlParam($param = null) {
//lets assume $internal_array is
//your object's internal array sent through urlParam()
if(is_null($param)) {
return $internal_array;
} else {
return isset($internal_array[$param]) ? $internal_array[$param] : '';
}
}
Doing this would let you you keep your existing code compatible with $this->urlParams()['some_param']usage while enabling you to refactor everything to $this->urlParams('some_param').
Here is a separate static function which returns NULL if value is not present in an array.
public static function value($key, $list, $default = NULL) {
if (is_array($list)) {
return array_key_exists($key, $list) ? $list[$key] : $default;
}
return $default;
}
Can call this everytime we need to use empty(), isset(), array_key_exists()
$this->someSortingFunction(value('orderby', $this->urlParams()));
What I'm trying to do is write one function to reuse vs writing out an if statement every time.
If statement:
if (!isset($value)){ echo 'null';}else{ echo $value;}
Function:
function isSetTest($value){
if ( !isset($value)){
$value = NULL;
}
return $value;
}
echo'name'.isSetTest($value);
Function works but I still get the "undefined" error message which is what i'm trying to avoid.
Pass by reference instead, so that no processing of the variable is done until you want it:
function isSetTest(&$value) { // note the &
if (!isset($value)) {
$value = NULL;
}
return $value;
}
You can shorten this a bit:
function isSetTest(&$value) {
return isset($value) ? $value : null;
}
I have a function that does something similar, except you can provide an optional default value in the case that the variable is not set:
function isset_or(&$value, $default = null) {
return isset($value) ? $value : $default;
}
The problem in your code is, that you still pass an undefined variable to your function, so that's why you still get your undefined error.
One way to solve this now, is that you pass your variable name as a string and then use variable variables, to check if it exists, e.g.
function isSetTest($value){
global $$value;
if ( !isset($$value)){
$$value = NULL;
}
return $$value;
}
echo'name'.isSetTest("value");
Demo
In javascript I can pass an object literal to an object as a parameter and if a value does not exist I can refer to a default value by coding the following;
this.title = params.title || false;
Is there a similar way to do this with PHP?
I am new to PHP and I can't seem to find an answer and if there is not an easy solution like javascript has, it seems pure crazy to me!!
Is the best way in PHP to use a ternary operator with a function call?
isset($params['title']) ? $params['title'] : false;
Thanks
Don't look for an exact equivalent, because PHP's boolean operators and array access mechanism are just too different to provide that. What you want is to provide default values for an argument:
function foo(array $params) {
$params += array('title' => false, ...);
echo $params['title'];
}
somethig like this $title = (isset($title) && $title !== '') ? $title : false;
Or using the empty function:
empty($params['title']) ? false : $params['title'];
$x = ($myvalue == 99) ? "x is 99": "x is not 99";
PHP one liner if ...
if ($myvalue == 99) {x is 99} else {x is not 99 //set value to false here}
<?php
class MyObject {
// Default value of object property
public $_title = null;
// Default value of argument (constructor)
public function __construct($title = null){
$this->_title = $title;
}
// Default value of argument (setter)
public function setTitle($title = null){
// Always validate arguments if you're serious about what you're doing
if(!is_null($title) and !is_string($title)){
trigger_error('$title should be a null or a string.', E_USER_WARNING);
return false;
}
$this->_title = $title;
return true;
}
} // class MyObject;
?>
This is how you do an object with default values. 3 ways in 1. You either default the property value in the class definition. Or you default it on the __construct assignment or in a specific setter setTitle.
But it all depends on the rest of your code. You need to forget JS in order to properly use PHP. This is a slightly stricter programming environment, even if very loose-typed. We have real classes in PHP, not imaginary function classes elephants that offer no IDE code-completion support like in JS.
I'm writing quite often this line of code:
$myParam = isset($params['myParam']) ? $params['myParam'] : 'defaultValue';
Typically, it makes the line very long for nested arrays.
Can I make it shorter?
function getOr(&$var, $default) {
if (isset($var)) {
return $var;
} else {
return $default;
}
}
$myParam = getOr($params['myParam'], 'defaultValue');
Be sure to pass the variable by reference though, otherwise the code will produce a E_NOTICE. Also the use of if/else instead of a ternary operator is intentional here, so the zval can be shared if you are using PHP < 5.4.0RC1.
PHP 7 will contain ?? operator that does exactly that.
See https://wiki.php.net/rfc/isset_ternary, example:
// Fetches the request parameter user and results in 'nobody' if it doesn't exist
$username = $_GET['user'] ?? 'nobody';
// equivalent to: $username = isset($_GET['user']) ? $_GET['user'] : 'nobody';
Yes, by making a proxy function, but is it really worth it?
Also, isset is a language construct, so wrapping it in a proxy function will degrade performance, although the degradation will likely be less than trivial (not even really worth mentioning.)
This is what I use:
function getindex($arr, $index, $default = null) {
return isset($arr[$index]) ? $arr[$index] : $default;
}
As of PHP 5.3 you can use:
$myParam = $params['myParam'] ?: 'defaultValue';
Note, however, that $params['myParam'] and isset($params['myParam']) are not 100% the same.
I'm using little this little magic class which works as variable
class Post() {
private $post = Array();
public function __construct() {
$this->post = $_POST;
}
public function __get($name) {
return #$this->post[$name];
}
public function __set($name, $value) {
return $this->post[$name] = $value;
}
public function __call($function, $params) {
if(isset($this->post[$function])) {
return $this->post[$function];
} else {
$this->post[$function] = $params[0];
return $params[0];
}
}
}
$post = new Post();
then in document you can use it easily as any other variable so for example $post->name $post->somelist[2] or with default value $post->name("John Doe") and after that you got it returned as well as stored.
I know this doesn't shorten anything up for you but thought I'd just share this, I use this alot in my applications to make sure something is set and has a value.
function is_blank($var = NULL){
return empty($var) && !is_numeric($var) && !is_bool($var);
}
function chk_var($var = NULL){
return (isset($var) && !is_null($var) && !is_blank($var));
}
Then...
if(chk_var($myvar)){ ... }
No. Unfortunately, you can't. Not in a decent way. You'll at least have to give in on performance.
Update: since PHP7, ?? will do just that. See https://wiki.php.net/rfc/isset_ternary
You if you have to do it often, you are probably missing the point.
In fact, variables should be defined before use.
So, there oughtn't be a case when you have your param undefined.
Just create a default params file, and initialize every your variable.
$params['myParam'] = 'defaultValue';
later it can be changed under some circunstances but it never be undefined.
Got the idea?