Testing if a class attribute is initialized with PHP

Published on 2021-12-23 • Modified on 2021-12-23

We test if a class attribute is initialized with PHP in this snippet. It may not be obvious; we all know how to test if a variable is set with the isset() function, but the check is not the same. We can also use the isset() function in this case! We can also use reflection with the ReflectionProperty::isInitialized() function. PHPStan isn't happy with this code and reports: Dead catch - Error is never thrown in the try block because of the try/catch block.


<?php

declare(strict_types=1);

// src/Controller/Snippet/Snippet180Trait.php

namespace App\Controller\Snippet;

use App\Samples\ProductSample;

/**
 * I am using a PHP trait to isolate each snippet in a file.
 * This code should be called from a Symfony controller extending AbstractController (as of Symfony 4.2)
 * or Symfony\Bundle\FrameworkBundle\Controller\Controller (Symfony <= 4.1).
 * Services are injected in the main controller constructor.
 */
trait Snippet180Trait
{
    public function snippet180(): void
    {
        // Here is what the ProductSample class looks like
        /**
        class ProductSample implements GroupSequenceProviderInterface
        {
            #[Assert\NotBlank(groups: ['foo'])]
            public string $foo;
         */
        $product = new ProductSample();

        // This raises an error as $product->foo isn't initialized
        try {
            if ($product->foo === 'bar') {
                echo '$product->foo:'.$product->foo;
            }
        } catch (\Error $e) {
            echo $e->getMessage().PHP_EOL;
        }

        // With isset() ————————————————————————————————————————————————————————
        if (isset($product->foo)) { // @phpstan-ignore-line
            echo '$product->foo is initalized.'.PHP_EOL;
        } else {
            echo '$product->foo is NOT initalized.'.PHP_EOL;
        }
        $product->foo = 'bar';

        if (isset($product->foo)) { // @phpstan-ignore-line
            echo '$product->foo is initalized.'.PHP_EOL;
        } else {
            echo '$product->foo is NOT initalized.'.PHP_EOL;
        }

        // With Reflection —————————————————————————————————————————————————————
        $product2 = new ProductSample();
        $rp = new \ReflectionProperty(ProductSample::class, 'foo');
        echo '$product2->foo initalized ? -> '.($rp->isInitialized($product2) ? 'true' : 'false').PHP_EOL;
        $product2->foo = 'bar';
        echo '$product2->foo initalized ? -> '.($rp->isInitialized($product2) ? 'true' : 'false').PHP_EOL;
    }
}

 Run this snippet  ≪ this.showUnitTest ? this.trans.hide_unit_test : this.trans.show_unit_test ≫  More on Stackoverflow   Read the doc  More on the web

<?php

declare(strict_types=1);

namespace App\Tests\Integration\Controller\Snippets;

use App\Samples\ProductSample;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;

/**
 * @see Snippet180Trait
 */
final class Snippet180Test extends KernelTestCase
{
    /**
     * @see Snippet180Trait::snippet180
     */
    public function testSnippet180IsSet(): void
    {
        $product = new ProductSample();
        self::assertFalse(isset($product->foo));

        $rp = new \ReflectionProperty(ProductSample::class, 'foo');
        self::assertFalse($rp->isInitialized($product));

        $this->expectException(\Error::class);
        if ($product->foo === 'bar') {
            self::assertSame('bar', $product->foo);
        }
    }

    /**
     * I have to ignore some PHPStan warnings for obvious reason.
     *
     * @see Snippet180Trait::snippet180
     */
    public function testSnippet180IsNotSet(): void
    {
        $product = new ProductSample();
        $product->foo = 'bar';
        self::assertTrue(isset($product->foo)); // @phpstan-ignore-line

        $rp = new \ReflectionProperty(ProductSample::class, 'foo');
        self::assertTrue($rp->isInitialized($product));

        if ($product->foo === 'bar') {  // @phpstan-ignore-line
            self::assertSame('bar', $product->foo);
        }
    }
}