Tester si un attribut d'une classe est initialisé avec PHP

Publié le 23/12/2021 • Mis à jour le 23/12/2021


English language detected! 🇬🇧

  We noticed that your browser is using English. Do you want to read this post in this language?

Read the english version 🇬🇧 Close

Dans ce bout de code, nous voyons comment tester si un attribut d'une classe PHP est initialisé. Ce n'est pas si évident qu'il n'y parait, nous savons tous comment tester l'existence d'une variable avec la fonction isset(), mais ici le test n'est pas vraiment identique. En fait, on peut aussi utiliser isset() dans ce cas. On peut aussi utiliser la réflexion avec la fonction ReflectionProperty::isInitialized(). Veuillez noter que PHPStan n'est pas très content de ce code et il reporte l'erreur suivante : Dead catch - Error is never thrown in the try block par rapport au bloc try / catch.


<?php

declare(strict_types=1);

// src/Controller/Snippet/Snippet180Trait.php

namespace App\Controller\Snippet;

use App\Samples\ProductSample;

/**
 * J'utilise un trait PHP afin d'isoler chaque snippet dans un fichier.
 * Ce code doit être apellé d'un contrôleur Symfony étendant AbstractController (depuis Symfony 4.2)
 * ou Symfony\Bundle\FrameworkBundle\Controller\Controller (Symfony <= 4.1).
 * Les services sont injectés dans le constructeur du contrôleur principal.
 */
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)) {
            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;
    }
}

 Exécuter le snippet  ≪ this.showUnitTest ? this.trans.hide_unit_test : this.trans.show_unit_test ≫  Plus sur Stackoverflow   Lire la doc  Plus sur le 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);
        }
    }
}