PhpStorm missing warning about invalid enum comparison - php

PhpStorm does not complain about this code:
enum UserType: string {
CASE ADMIN = 'admin';
}
function doSomething (UserType $userType) {
if('someInvalidType' == $userType) {
echo 'test';
}
}
It only complains when using ===. How does it try to cast the enum when only using ==? This behavior seems weird. I think it should also indicate a problem when == is used.

Related

PHP boolean in class being a fatal error

I was working with php Booleans in a class when I noticed everytime I tried to make one I got an error here is a smaller version of my code
class my_class
{
public $hide_image == true;
}
thats the part of my code thats failing, without that boolean it works fine, but nothing on the page will show up when its there. How can I fix this? I am doing something wrong?
== compares the values of variables for equality. = sets.
public $hide_image = true;
http://php.net/manual/en/language.operators.comparison.php
The mistake that you using comparison operator "==" instead of "=". As a result your public property looks like that:
class my_class
{
public true;
}
Right:
class my_class
{
public $hide_image = true;
}
You are using double ==.
you should only use 1 = to assign a value
Try:
class my_class
{
public $hide_image = true;
}

Cannot pass null argument when using type hinting

The following code:
class Type {
}
function foo(Type $t) {
}
foo(null);
failed at run time:
PHP Fatal error: Argument 1 passed to foo() must not be null
Why is it not allowed to pass null just like other languages?
PHP 7.1 or newer (released 2nd December 2016)
You can explicitly declare a variable to be null with this syntax
function foo(?Type $t) {
}
this will result in
$this->foo(new Type()); // ok
$this->foo(null); // ok
$this->foo(); // error
So, if you want an optional argument you can follow the convention Type $t = null whereas if you need to make an argument accept both null and its type, you can follow above example.
You can read more here.
PHP 7.0 or older
You have to add a default value like
function foo(Type $t = null) {
}
That way, you can pass it a null value.
This is documented in the section in the manual about Type Declarations:
The declaration can be made to accept NULL values if the default value of the parameter is set to NULL.
Starting from PHP 7.1, nullable types are available, as both function return types and parameters. The type ?T can have values of the specified Type T, or null.
So, your function could look like this:
function foo(?Type $t)
{
}
As soon as you can work with PHP 7.1, this notation should be preferred over function foo(Type $t = null), because it still forces the caller to explicitly specify an argument for the parameter $t.
Try:
function foo(Type $t = null) {
}
Check out PHP function arguments.
As of PHP 8.0 (released November 26, 2020), you can also use the nullable union types.
This means that you are allowed to pass either Type or null as the parameter value:
class Type {}
function foo(Type|null $param) {
var_dump($param);
}
foo(new Type()); // ok : object(Type)#1
foo(null); // ok : NULL
Read more about union types.
As other answers already mentioned, this is only possible if you specify null as the default value.
But the cleanest type-safe object oriented solution would be a NullObject:
interface FooInterface
{
function bar();
}
class Foo implements FooInterface
{
public function bar()
{
return 'i am an object';
}
}
class NullFoo implements FooInterface
{
public function bar()
{
return 'i am null (but you still can use my interface)';
}
}
Usage:
function bar_my_foo(FooInterface $foo)
{
if ($foo instanceof NullFoo) {
// special handling of null values may go here
}
echo $foo->bar();
}
bar_my_foo(new NullFoo);
In my case, the problem was the native "trim" function, that not accepts null.
Let's suppose that you've this code:
if (trim($tables) != '')
{
//code
}
PHP8 will throws you this error; so you if you're working on a legacy code, I suggest you to create a custom "trim" function, like this one, to make it work quickly.
public function custom_trim(?string $value)
{
return trim($value ?? '') ;
}
I really hate this change from 7.4 to 8

Fatal error: Can't use method return value in write context in <filename> on line <ifconditioin statement>

I get the error for the following code
class Myclass {
//...variables
public function getName() {
return $this->strName;
}
public function checkDup() {
if(empty($this->getName())) { //HERE IS THE ERROR
$strMessage = 'Please Enter First Name';
return $strMessage;
}
}
}
$a = new Myclass (); //assume constructor is present and variables are set in class
$a->checkDup();
What could be the solution?
My getName() function returns the name of the variable
I figured out what was going wrong here.
I should be referring to the class member variable using $this and not call the getName() function again inside the class. This changes the function to :
public function checkDup() {
if(empty($this->strName)) { //HERE IS THE SOLUTION
$strMessage = 'Please Enter First Name';
return $strMessage;
}
}
change:
if(empty($this->getName())) {
to
$name = $this->getName();
if( empty($name) ) {
...
empty() only checks variables as anything else will result in a parse error
the empty construct is a bit silly that way. see the manual:
Note:
Prior to PHP 5.5, empty() only supports variables; anything else will
result in a parse error. In other words, the following will not work:
empty(trim($name)). Instead, use trim($name) == false.
I don't like any of the answers here. Yes you can reference $this->strName directly. But what if you can't? What if getName() has some important functionality?
Then the only option is to create another variable? $var = $this->getName();.
That also sucks.
Generically whenever I access a class property or function dynamically I enclose it in braces {} so:
I'll know it's dynamic.
I'll get meaningful errors if there are any.
Examples:
$this->{$variable};
$this->{$class->property};
$this->{$class->method()};
$this->{function()};
$this->{$array['index']};

Default value if variable not sets

I sometimes have variables that might not be set and I would like to use a default parameter instead. Like here:
if ($p == "a") doSomething();
If $p is not defined PHP throws Notice: Undefined variable. To avoid this I often I used this construct in such a case:
$p = (isset($p) ? $p : "");
But that is ugly if you have to use it a lot. So I wrote a function for it:
function getIfSet(&$value, $default = '')
{
return isset($value) ? $value : $default;
}
// Example
if (getIfSet($p) == "a") doSomething();
I wonder if there is a PHP function for this or how you solve this.
Just a little improvement, prefer passing null value to $default, passing empty string can be confusing, cause correct value can be empty string.
function getIfSet(&$value, $default = null)
{
return isset($value) ? $value : $default;
}
$p = getIfSet($p);
isset() is about as clean as it gets. Although I must admit that I'm not too fond of defaulting to an empty string, simply because a variable could be an empty string, yet still "be set". I think that a default of bool false or null would be truer to the behavior of isset:
function getIfSet(&$value, $default = false)
{
return isset($value) ? $value : $default;
}
$p = getIfSet($p);
if($p !== false){
//insert or whatever
}
else{
header('Location: error.php');
exit;
}
Depending on what kind of values you're checking (maybe REQUEST data?), consider using classes. They are fun and they could be available anywhere.
Assuming you're checking POST data (if you don't, well, take this as an idea), create a class that checks this array:
class Post
{
public function __get($index)
{
if (isset($_POST[$index]))
return $_POST[$index];
else
return null;
}
}
As simple as that. You know that __get() will trigger when you try to access a non-existant property. In this case, if the property (actually, the index in the $_POST array) doesn't exist, null will be returned and no errors are generated.
Now you can do:
$params = new Post();
$foo = $params->name ?: ''; // of course this doesn't make much sense.
if (!$params->password) ...
// instead of
if (isset($_POST['password'])) ...
// you'll still have to use isset for cases like:
if (isset($_POST['user']['password']) ...
if (isset($params->user['password'])) ...
// but still looks neater I'd say
A problem you'll find soon is that $params isn't a super global variable, while $_POST are. How to solve this? Create it in the constructor of your controller class and use Dependency Injection for all other objects your are using.
I tried to make renocor's answer more clean and OOP when I came up with this solution:
class NiceArray implements ArrayAccess {
protected $array;
public function __construct(&$array) {
$this->array =& $array;
}
public function offsetExists($offset) {
return true;
}
public function offsetGet($offset) {
if (isset($this->array[$offset]))
{
return $this->array[$offset];
}
else
{
return null;
}
}
public function offsetSet($offset, $value) {
$this->array[$offset] = $value;
}
public function offsetUnset($offset) {
unset($this->array[$offset]);
}
}
Usage:
$get = new NiceArray($_GET);
if ($get['p'] == "a") doSomething();
I know the class is kind of big but this way you still have an array and you can easily use it for every array you want. You do not need to change any code you may had before. You can still access and change the data. It will even change the original array.

How do you enforce your PHP method arguments?

How do you validate/manage your PHP method arguments and why do you do it this way?
Well, assuming that you're talking about type-checking method arguments, it depends:
If it's expecting an object, I use type-hinting with an interface:
public function foo(iBar $bar)
If it's expecting an array only, I use type-hinting with the array keyword.
public function foo(array $bar)
If it's expecting a string, int, bool or float, I cast it:
public function foo($bar) {
$bar = (int) $bar;
}
If it's expecting mixed, I just check in a cascade:
public function foo($bar) {
if (is_string($bar)) {
//handle string case
} elseif (is_array($bar)) {
//...
} else {
throw new InvalidArgumentException("invalid type");
}
}
Lastly, if it's expecting an iterable type, I don't use type-hinting. I check if it's an array first, then re-load the iterator:
public function foo($bar) {
if (is_array($bar)) {
$bar = new ArrayIterator($bar);
}
if (!$bar instanceof Traversable) {
throw new InvalidArgumentException("Not an Iterator");
}
}
If it's expecting a filename or directory, just confirm it with is_file:
public function foo($bar) {
if (!is_file($bar)) {
throw new InvalidArgumentException("File doesn't exist");
}
}
I think that handles most of the cases. If you think of any others, I'll gladly try to answer them...
Typechecking is something you should do at the development stage, not in production. So the appropriate syntactic feature for that would be:
function xyz($a, $b) {
assert(is_array($a));
assert(is_scalar($b));
However I'll try to avoid it, or use type coercion preferrably. PHP being dynamically typed does quite well adapting to different values. There are only few spots where you want to turndown the basic language behaviour.

Categories