Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

empty() and isset() returning incorrect result for properties of an object - PHP 7.3, Laravel 5.6

Environment: PHP 7.3, Laravel 5.6

Problem: empty() and isset() are returning an incorrect result when the code runs in a browser, but the correct result when run over a Tinker command line session.


Expected Behavior:

  • isset() to return true when the property exists, false when it does not
  • empty() to return true when the property exists and is not null, false otherwise

Example: empty()

if(!empty($practiceArea->hero_video)) { 
   ... some HTML
}

This !empty always evaluates to false, even though $practiceArea->hero_video is set and I can see its value through echo or var_dump.

And empty($practiceArea->hero_video) always evaluates to true, as I learned after unsuccessfully trying the alternative:

if(empty($practiceArea->hero_video) === false) {

Example: isset()

isset($practiceArea->hero_video) always incorrectly returns false.


Current Hacky Workaround:

$video = $practiceArea->hero_video;
if(!empty($video)) { 
   ... some HTML
}

This works exactly as expected - $video takes the value of $practiceArea->hero_video, then !empty($video) returns true if we have a value and false if the value is null.


Examples of expected result in Tinker session:

>>> $pa = PracticeArea::find(11)
>>> ... an object is returned
>>> $pa->hero_video
=> "//www.youtube.com/embed/0qisGSwZym4"
>>> if(empty($pa->hero_video)) echo "Empty"; else echo "Not empty";
Not empty
>>> if(!empty($pa->hero_video)) echo "Not empty"; else echo "Empty";
Not empty

isset() also works as expected in Tinker:

>>> isset($pa->hero_video)
=> true
>>> !isset($pa->hero_video)
=> false

Reproducing:

I assume that our PracticeArea class is using magic getters, because it extends Laravel 5.6's Model class, which does include __get and __isset methods.

Below is the abridged PracticeArea class. Happy to post the whole thing, but there are no methods or properties relevant to this.

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;
use TCG\Voyager\Traits\Translatable;

class PracticeArea extends Model
{

    protected $table = 'practice_areas';

}


var_dump($practiceAreas)

object(App\PracticeArea)#506 (29) { 
    ["translatable":protected]=> array(5) { 
        [0]=> string(4) "name" 
        [1]=> string(11) "description" 
        [2]=> string(7) "heading" 
        [3]=> string(4) "body" 
        [4]=> string(4) "slug" 
    } 
    ["table":protected]=> string(14) "practice_areas" 
    ["fillable":protected]=> array(11) { 
        [0]=> string(25) "practice_area_category_id" 
        [1]=> string(11) "metadata_id" 
        [2]=> string(4) "name" 
        [3]=> string(11) "description" 
        [4]=> string(7) "heading" 
        [5]=> string(4) "body" 
        [6]=> string(10) "sort_order" 
        [7]=> string(9) "published" 
        [8]=> string(10) "hero_video" 
        [9]=> string(10) "hero_image" 
        [10]=> string(4) "slug" 
    } 
    ["translates":protected]=> array(5) { 
        [0]=> string(4) "name" 
        [1]=> string(11) "description"
        [2]=> string(7) "heading" 
        [3]=> string(4) "body" 
        [4]=> string(4) "slug" 
    } 
    ["translate_relation":protected]=> string(14) "practice_areas" 
    ["connection":protected]=> string(5) "mysql" 
    ["primaryKey":protected]=> string(2) "id" 
    ["keyType":protected]=> string(3) "int" 
    ["incrementing"]=> bool(true) 
    ["with":protected]=> array(0) { } 
    ["withCount":protected]=> array(0) { } 
    ["perPage":protected]=> int(15) 
    ["exists"]=> bool(true) 
    ["wasRecentlyCreated"]=> bool(false) 
    ["attributes":protected]=> array(15) { 
        ["id"]=> int(11) 
        ["practice_area_category_id"]=> int(1) 
        ["metadata_id"]=> int(26) 
        ["name"]=> string(19) "Workplace Accidents" 
        ["description"]=> string(30) "Workplace injuries description" 
        ["heading"]=> string(30) "Workplace Accidents & Injuries" 
        ["body"]=> string(1246) "
like image 750
Nathan Avatar asked Oct 14 '25 03:10

Nathan


1 Answers

isset() on non-public class properties need an implementation of public function __isset() in the class to work properly.

http://php.net/manual/en/language.oop5.overloading.php#object.isset

__isset() is triggered by calling isset() or empty() on inaccessible properties.

Your specific problem is coming from the implementation in Laravel:

public function __isset($key)
{
    return $this->offsetExists($key);
}

public function offsetExists($offset)
{
    return ! is_null($this->getAttribute($offset));
}

The method getAttribute() does not inspect all class properties plus it checks for ! is_null which is not fully equivalent to isset() / empty().

https://github.com/laravel/framework/blob/5.6/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php

// If the attribute exists in the attribute array or has a "get" mutator we will
// get the attribute's value. Otherwise, we will proceed as if the developers
// are asking for a relationship's value. This covers both types of values.
if (array_key_exists($key, $this->attributes) ||
    $this->hasGetMutator($key)) {
    return $this->getAttributeValue($key);
}

I would try to investigate the content of the $attributes property from that Trait.

like image 157
Daniel W. Avatar answered Oct 18 '25 04:10

Daniel W.



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!