30

Is there any way to mark a magic property as deprecated? Consider following, simplified code:

/**
 * Example class
 *
 * @property string $foo A foo variable.
 */
class Example {
    /**
     * Magic getter
     */
    public function __get($var) {
        if('foo' === $var) {
            // do & return something
        }
    } 
}

Now, how to indicate other developers, that they should not use Example::$foo anymore? The only working solution that comes to my mind is:

/**
 * Example class
 */
class Example {
    /**
     * A foo variable.
     *
     * @var string
     * @deprecated
     */
    public $foo;

    /**
     * Magic getter
     */
    public function __get($var) {
        if('foo' === $var) {
            // do & return something
        }
    } 
}

But this both breaks my code (getter is not called) and doesn't feel very elegant.

ptkoz
  • 2,388
  • 1
  • 20
  • 28
  • throw a warning, or an exception, and document it? – Ryan May 31 '16 at 19:43
  • [Seems legit to me](https://manual.phpdoc.org/HTMLSmartyConverter/HandS/phpDocumentor/tutorial_tags.deprecated.pkg.html) - doesn't compile? – scrowler May 31 '16 at 19:45
  • @self I don't want to break old dependencies - just indicate, that it shouldn't be used in newer code. – ptkoz May 31 '16 at 19:48
  • @RobbieAverill Magic __get() method is not called when I explicitly declare public property inside class. – ptkoz May 31 '16 at 19:55
  • @pamelus as a getter/setter pattern your properties should not be public, but I don't think that will affect your result – scrowler May 31 '16 at 19:57
  • hhvm has attributes, there is an RFC for php – Ryan May 31 '16 at 19:58
  • 4
    shouldn't you set it private so the magic getter is still used ? the public context won't call the magic method. afaik. you got 3 choices: property does not exists -> property overloading -> calls the magic context property is private and is not accessed from inside of the class __get() is called explicit – shadowdroid May 31 '16 at 19:59
  • @shadowdroid Still it doesn't feel very elegant. Also some (most?) of the IDEs do not provide code completion (and deprecation warning) for private members outside of class scope. So, maybe this the best way? To remove `@property` completely and indicate deprecation by not providing such field for code completion / documentation at all? – ptkoz May 31 '16 at 20:12
  • @pamelus I would do so. But my personal opinion is the solution of pieter (below) is "the cleanest" since you indicate "something is wrong". If you just remove the property and "hide" it some programmer might find some old overloading remnant and think "cool this works". And use it out of commodity. Pieters solution does not solve this either but at least you did your best to warn the other developers. This is about taste I guess :) – shadowdroid May 31 '16 at 20:20

3 Answers3

18

The @mixin approach works at least with PhpStorm:

/**
 * class or trait for the {@mixin} annotation
 */
trait DeprecatedExampleTrait {

    /**
     * Declare it as private to increase the warning level
     * @deprecated
     * @var string
     */
    public $foo;
}

/**
 * Example class
 *
 * @mixin DeprecatedExampleTrait
 *
 * @property string $newFoo A foo variable.
 */
class Example {
    /**
     * Magic getter
     */
    public function __get($var) {
        if (in_array($var, ['foo', 'newFoo'])) {
            // do & return something
        }
    }
}

$example = new Example;
$example->foo;

Screenshot:

PhpStorm Screenshot

JonathanDavidArndt
  • 2,518
  • 13
  • 37
  • 49
kaluzki
  • 331
  • 2
  • 5
13

This is not possible with PHPDoc as the @deprecated can only be associated with structural elements (documentation).

If it is really important for developers to know that they should no longer use this magic property, you could trigger an E_USER_DEPRECATED error:

/**
 * Example class
 *
 * @property string $foo A foo variable.
 */
class Example {

    public function __get($name)
    {
        if ($name === 'foo') {
            trigger_error('Property $foo is deprecated and should no longer be used', E_USER_DEPRECATED);
        }
        // ...
    }
}
scrowler
  • 24,273
  • 9
  • 60
  • 92
Pieter
  • 1,764
  • 1
  • 12
  • 16
  • A property [is a structural element](https://phpdoc.org/docs/latest/glossary.html#term-structural-elements). – scrowler May 31 '16 at 19:52
  • 4
    Yes, but the question here is if you can associate a `@deprecated` with a `@property`, which is not possible as you can only associate `@deprecated` with *real* properties/classes/interfaces/methods – Pieter May 31 '16 at 19:54
  • Yeah, your example doesn't match the question though. In the question, it is a *real* property – scrowler May 31 '16 at 19:56
  • 1
    Please read the question again @RobbieAverill as the question is clearly about the use of `@property`. @pamelus is trying to avoid having to use a real property as it will break his code. – Pieter May 31 '16 at 19:58
  • I see, so the OP is trying to document a property that doesn't exist. In that case I would tend to suggest creating a concrete `getFoo()` method and deprecating *that*, rather than overriding the `__get()` method. You don't need to trigger any errors to notify a user to stop using something - a deprecation tag should be sufficient – scrowler May 31 '16 at 20:06
  • Here's a little complement, just in case: http://php.net/manual/pt_BR/errorfunc.constants.php (Complete list of error constants from docs) – CarlosCarucce May 31 '16 at 20:12
  • I'm a bit afraid of flooding logs in legacy dependent packages. – ptkoz May 31 '16 at 20:19
  • Switching to a `getFoo()` usage would require calling code to change from `$this->foo` to `$this->getFoo()`, which is probably a dealbreaker. – ashnazg Dec 08 '16 at 22:12
-1

To prevent users from using your deprecated property, you can just remove the PHPDoc for this property from your class header.

/**
  * Example class
  *
  */
 class Example {
     /**
      * Magic getter
      */
     public function __get($var) {
         if('foo' === $var) {
             // do & return something
         }
     } 
 }

This way, you'll keep your legacy code working, while the property will no longer be shown by IDE autocompletion tools etc.

  • This answer is already in the accepted answer – Isaac Apr 12 '21 at 02:37
  • 1
    @Isaac, the accepted answer involves creating an (otherwise unused) trait and including it in the main class' PHPDoc. While my answer is all about removing the deprecated property from PHPDoc. So no, these two are totally different approaches. – Ilya Vikharev Apr 15 '21 at 14:05
  • This would make the magic prop start showing as unrecognized by IDEs, instead of actually serving the purpose of documenting it while it still exists (and potentially why it's getting deprecated). – igorsantos07 Nov 05 '21 at 04:31