[PHP] Modification d'une propriété privée de classe à l'exécution

Publié le 03/12/2019 • Actualisé le 03/02/2020


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 allons voir comment modifier une propriété privée d'un objet PHP. J'ai récemment eu un cas d'utilisation concret quand j'ai du créer ma configuration pour le bundle EasyDeploy. J'ai eu à modifier une propriété privée de l'objet DefaultConfiguration pour que le déploiement puisse fonctionner.
[Maj du 03/02/2020] Ajout de tests unitaires et amélioré l'affichage des résultats.


<?php

declare(strict_types=1);

namespace App\Controller\Snippet;

use Symfony\Component\HttpFoundation\Request;

/**
 * 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 Snippet61Trait
{
    public function snippet61(Request $request): void
    {
        $class = \get_class($request);
        echo \sprintf('> Request value object class: %s'.PHP_EOL, $class);

        // This code "$request->isHostValid" would provoke a PHP error as the "isHostValid"
        // is private (check out the unit tests)

        $property = (new \ReflectionClass($request))->getProperty('isHostValid');
        $property->setAccessible(true); // now we can read and write "isHostValid"
        echo '> isHostValid: '.((bool) $property->getValue($request) ? 'true' : 'false').PHP_EOL;

        $property->setValue($request, false);
        echo '> isHostValid: '.((bool) $property->getValue($request) ? 'true' : 'false');

        // That's it! 😁
    }
}

 Exécuter le snippet  ≪ this.showUnitTest ? this.trans.hide_unit_test : this.trans.show_unit_test ≫  Plus sur Stackoverflow   Lire la doc  Snippet aléatoire

  Travaillez avec moi !

<?php

declare(strict_types=1);

namespace App\Tests\Integration\Controller\Snippets;

use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Symfony\Component\HttpFoundation\Request;

/**
 * @see Snippet74Trait
 */
final class Snippet61Test extends KernelTestCase
{
    /**
     * @see Snippet61Trait::snippet61
     */
    public function testSnippet61(): void
    {
        $request = Request::create('/en/snippets/modifying-a-private-class-property-at-runtime');
        $property = (new \ReflectionClass($request))->getProperty('isHostValid');

        // As of PHP 8.1, private properties are accessible with reflection!
        // @see https://phpbackend.com/blog/post/php-8-1-accessing-private-protected-properties-methods-via-reflection-api-is-now-allowed-without-calling-setAccessible
        if (PHP_VERSION_ID < 80100) {
            try {
                self::assertNull($property->getValue($request));
                self::fail('A ReflectionException should be raised!');
            } /* @noinspection PhpRedundantCatchClauseInspection */
            catch (\ReflectionException) {
            }
        }

        $property->setAccessible(true);
        self::assertTrue($property->getValue($request));
        $property->setValue($request, false);
        self::assertFalse($property->getValue($request));
    }
}