I get a lot of array from some API and I need to check weither some variable exist or not.
I have a lot of block that look like that :
if (isset($var))
$varToSet = $var;
else
$varToSet = '';
So I've decided to make a function for that. I came with that:
function setVar($var)
{
if (isset($var))
return $var;
return '';
}
But as I would expected I got the error Undefined variable, I figured out I needed to passe the argument by reference so I would get the following prototype :
function setVar(&$var);
And It was working perfectly until now, here's an example of my problem :
// works fine
$var = "test";
$varToSet = setVar($var);
// works fine
$var = "test";
$varToSet = setVar($doesNotExist);
// works fine
$var = "test";
$varToSet = setVar($doesNotExist['index']);
// doesn't work
$var = "test";
$varToSet = setVar($var['index']);
In the last example I get Illegal string offset 'index and Only variables can be passed by reference PHP errors.
I know why I got those errors, I just can't figure out how overcome this problem.
i mainly use property_exists to check if a value exist on a json object.
function getFromJson($json,$value)
{
if (property_exists(json, $value)) {
return $json->$value;
}
return null;
}
function get($var,$value = null)
{
if (is_null($value)) {
return $var;
}
if (is_object($var) && property_exists($var, $value)) {
return $json->$value;
}
if (is_array($var)) {
return $var[$value];
}
return $var;
}
The error gives you the answer. Your variable is a string. But you are trying to access an array element by using brackets [ ].
And the second is caused by invalid refference.
This is passing by reference:
$variable = 'test';
myFunction($variable);
and this is passing by value:
myFunction('test');
That's a big difference!
You can't call string as array
$varToSet = setVar($var['index']);
You can change the line to:
echo $var['index'];
and you will still have the same error/warning.
If you want to validate if array variable is set use
isset($var['index'])
but it returns value, not a refference
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.
Say I got some function that run some code and then return something, like this:
function something()
{
//some code
return $some[$whatever];
}
So, if I want to extract the data I generated in the function - the new value for $some, how should I do it? for example this won't do anything:
echo ($some);
Or what am I missing here, please
Since your Function returns a value, You may need to catch & store it inside a variable and then echo the variable if it is a String or do some casting to that effect. Here's an example:
<?php
function something(){
//some code
$whatever = 3;
$some = ["Peace", "Amongst", "All", "Humanity"];
return $some[$whatever];
}
$var = something();
var_dump($var); //<== DUMPS :: "Humanity"
echo $var; //<== ECHOES:: "Humanity"
Test it out here.
Cheers and Good Luck....
You are trying to return a specif key from your array, which wasn't declared. I declared an array for you, and I added the isset to check if the key is existing in the array to prevent any php warnings.
function something($findKey)
{
$some = array('key'=> 123);
if(!isset($some[$findKey])) {
return false;
}
//some code
return $some[$findKey];
}
echo something('key');
I'm working with a decoded JSON and I want to check if a field exists.
This is what I have:
$data = json_decode($json);
if(!$var = $data->{'var'})
return false;
else
return $var;
With this code, I get Notice: Undefined property: stdClass::$var in .... It works, and I know I can get rid of Notices by handling the error_reporting, but I prefer not to get that notice, so I did the following:
$data = json_decode($json);
if(!isset($data->{'var'}))
return false;
else {
$var = $data->{'var'};
return $var;
}
In this case I don't get the notice, but I have to first check if isset and then create a variable containing that data, which doesn't seem nice. I tried:
$data = json_decode($json);
if(!isset($var = $data->{'var'}))
return false;
else {
return $var;
}
But then I get a syntax error (syntax error, unexpected '=', expecting ',' or ')'). So is there any proper way of doing this in PHP? Or should I just forget it and keep with opt.#2?
Option 2 is the best of your suggestions, and indeed probably the best way to handle it if you only need to check one or two variable.
You can shorten it a little with a ternary though:
$data = json_decode($json);
return isset($data->{'var'})? $data->{'var'} : false;
If you need to do multiple checks, write an abstraction:
function tryGetObjectProperty($obj, $prop, $default = false){
return isset($obj->{$prop})? $obj->{$prop} : $default;
}
$data = json_decode($json);
$var1 = tryGetObjectProperty($data, 'var');
var2 = tryGetObjectProperty($data, 'var2');
//etc
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
What does the & before the function name signify?
Does that mean that the $result is returned by reference rather than by value?
If yes then is it correct? As I remember you cannot return a reference to a local variable as it vanishes once the function exits.
function &query($sql) {
// ...
$result = mysql_query($sql);
return $result;
}
Also where does such a syntax get used in practice ?
Does that mean that the $result is returned by reference rather than by value?
Yes.
Also where does such a syntax get used in practice ?
This is more prevalent in PHP 4 scripts where objects were passed around by value by default.
To answer the second part of your question, here a place there I had to use it: Magic getters!
class FooBar {
private $properties = array();
public function &__get($name) {
return $this->properties[$name];
}
public function __set($name, $value) {
$this->properties[$name] = $value;
}
}
If I hadn't used & there, this wouldn't be possible:
$foobar = new FooBar;
$foobar->subArray = array();
$foobar->subArray['FooBar'] = 'Hallo World!';
Instead PHP would thrown an error saying something like 'cannot indirectly modify overloaded property'.
Okay, this is probably only a hack to get round some maldesign in PHP, but it's still useful.
But honestly, I can't think right now of another example. But I bet there are some rare use cases...
Does that mean that the $result is returned by reference rather than by value?
No. The difference is that it can be returned by reference. For instance:
<?php
function &a(&$c) {
return $c;
}
$c = 1;
$d = a($c);
$d++;
echo $c; //echoes 1, not 2!
To return by reference you'd have to do:
<?php
function &a(&$c) {
return $c;
}
$c = 1;
$d = &a($c);
$d++;
echo $c; //echoes 2
Also where does such a syntax get used in practice ?
In practice, you use whenever you want the caller of your function to manipulate data that is owned by the callee without telling him. This is rarely used because it's a violation of encapsulation – you could set the returned reference to any value you want; the callee won't be able to validate it.
nikic gives a great example of when this is used in practice.
<?php
// You may have wondered how a PHP function defined as below behaves:
function &config_byref()
{
static $var = "hello";
return $var;
}
// the value we get is "hello"
$byref_initial = config_byref();
// let's change the value
$byref_initial = "world";
// Let's get the value again and see
echo "Byref, new value: " . config_byref() . "\n"; // We still get "hello"
// However, let’s make a small change:
// We’ve added an ampersand to the function call as well. In this case, the function returns "world", which is the new value.
// the value we get is "hello"
$byref_initial = &config_byref();
// let's change the value
$byref_initial = "world";
// Let's get the value again and see
echo "Byref, new value: " . config_byref() . "\n"; // We now get "world"
// If you define the function without the ampersand, like follows:
// function config_byref()
// {
// static $var = "hello";
// return $var;
// }
// Then both the test cases that we had previously would return "hello", regardless of whether you put ampersand in the function call or not.