How to deprecate PHP's magic property in PHPDoc? - php

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.

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:

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);
}
// ...
}
}

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.

Related

How to use if in stubs in laravel?

I have an artisan command which gets some options, one of these options is --type=, like below:
protected $signature = 'make:procedure {name} {--type=}';
--type= contains the kind of difference, I want to check this option in the stub because each type has a different namespace which should be used in the stub.
for example, this is my stub:
<?php
namespace DummyNamespace;
class DummyClass
{
//
}
How can I do this, (of course this is an example, I just trying to explain my problem):
<?php
namespace DummyNamespace;
if ($type === 'one') {
echo 'use App\Some\Namespace\One'
}
class DummyClass
{
//
}
It would be highly appreciated if anyone can advise me!😊
your Custom command should derive from GeneratorCommand then you can use abstract Method getStub()
Your Stub File
namespace DummyNamespace;
/**
* Class DummyClass.
*/
class DummyClass
{
}
In Your Command File, you just need to use below code
/**
* Get the stub file for the generator.
*
* #return string
*/
protected function getStub()
{
return app_path('file/path/test.stub');
}
For Explanation Only
In GeneratorCommand class
/**
* Get the stub file for the generator.
*
* #return string
*/
abstract protected function getStub();
/**
* Build the class with the given name.
*
* #param string $name
* #return string
*
* #throws \Illuminate\Contracts\Filesystem\FileNotFoundException
*/
protected function buildClass($name)
{
$stub = $this->files->get($this->getStub());
return $this->replaceNamespace($stub, $name)->replaceClass($stub, $name);
}
A very simple way to generate files from stub.
In your stub file
namespace {{namespace}};
/**
* Class {{name}}.
*/
class {{name}}
{
}
Somewhere in your command
protected function getStub()
{
return file_get_contents(resource_path('stubs/dummy.stub'));
}
protected function generate($namespace, $name)
{
$template = str_replace(
['{{namespace}}', '{{name}}'],
[$namespace, $name],
$this->getStub()
);
file_put_contents(app_path("Dummies/$name.php"), $template);
}

Proper phpdoc comment for iteratable object?

I'm having a bit of a problem trying to get a correct autocompletion for the following code example. I'm using PHPStorm 7 on a Win7 machine.
First just a simple class.
/**
* Class myObject
*/
class myObject
{
/**
* some method
*/
public function myMethod()
{
// do something
}
}
This one is the collection class which can contain multiple instances of the prior class and implements the IteratorAggregate interface.
/**
* Class myCollection
*/
class myCollection implements IteratorAggregate
{
/**
* #var myObject[]
*/
protected $_objects = array();
/**
* #param myObject $object
* #return myCollection
*/
public function add(myObject $object)
{
$this->_objects[] = $object;
return $this;
}
/**
* #return ArrayIterator
*/
public function getIterator()
{
return new ArrayIterator($this->_objects);
}
}
And here is the code example.
$collection = new myCollection;
$collection->add(new myObject);
$collection->add(new myObject);
foreach ($collection as $object) {
$object->myMethod(); // gets no autocompletion
}
As you may have guessed (and read in the example) the myMethod() call gets not autocompleted and is beeing listed in the code analysis. The only way i found is adding a comment block for $object, which i find, to be honest, extremely annoying.
/** #var $object myObject */
foreach ($collection as $object) {
$object->myMethod(); // gets autocompletion now, but sucks
}
So, any ideas or fundamented knowledge on how to solve this?
/**
* #return ArrayIterator|myObject[]
*/
public function getIterator()
{
return new ArrayIterator($this->_objects);
}
For extended classes (the base class is above):
/**
* #method myObject[] getIterator()
*/
class ExtendedClass extends BaseCollection
{
}
or
/**
* #method iterable<myObject> getIterator()
*/
class ExtendedClass extends BaseCollection
{
}
I think this will be best way to handle such case. at least it works with PHPStorm
Your
/** #var $object myObject */
block is indeed the correct way to accomplish this. The syntax you are expecting to do the work,
/**
* #var myObject[]
*/
is not standard phpdoc notation, although it is in informal use and has some effort ongoing to standardize. Until such standardization does happen, IDEs recognizing it will probably be hit-or-miss. IDE coverage of your $object local var block is also hit-or-miss, actually.
In your myCollection class, override current() as follows:
/** #return myObject */
public function current() {
return parent::current();
}
Possible workaround (also ugly) is to create static "constructor", that will return myObject. At least it works in eclipse. If you want to see collection methods too, then just add myCollection to return as "#return myObject[]|myCollection"
class myCollection implements \IteratorAggregate
{
/**
* #return myObject[]
*/
public function create()
{
return new static();
}
}

PHPUnit Mockupbuilder passing return value correctly

I found some strange behavior with mockup builder, can someone explain to me why this happen?
here is my test code:
class PlaceTest extends \PHPUnit_Framework_TestCase
{
const API_KEY = 'test-api';
public function testConstruct()
{
$google = $this->getMockBuilder('GusDeCooL\GooglePhp\Google')
->setConstructorArgs(array(self::API_KEY))
->setMethods(array('getKey'))
->getMock();
$google->expects($this->any())
->method('getKey')
->will($this->returnValue(self::API_KEY));
/* #var $google \GusDeCooL\GooglePhp\Google */
$place = new Place($google);
$this->assertInstanceOf('GusDeCooL\GooglePhp\Component\Place\Place', $place);
$this->assertInstanceOf('GusDeCooL\GooglePhp\Google', $place->getParent());
$this->assertEquals(self::API_KEY, $place->getKey());
return $place;
}
/**
* #param Place $place
*
* #depends testConstruct
*/
public function testGetKey(Place $place)
{
$this->assertInstanceOf('GusDeCooL\GooglePhp\Google', $place->getParent());
$this->assertEquals(self::API_KEY, $place->getKey());
}
}
And here is the code of actual class
<?php
namespace GusDeCooL\GooglePhp\Component\Place;
use GusDeCooL\GooglePhp\Component\ChildInterface;
use GusDeCooL\GooglePhp\Google;
use GusDeCooL\GooglePhp\Place\Nearby;
class Place implements ChildInterface
{
/**
* #var Google
*/
private $parent;
/**
* #var Nearby
*/
private $nearby;
public function __construct(Google $parent)
{
$this->setParent($parent);
}
/**
* API Key
* #return string
*/
public function getKey()
{
return $this->getParent()->getKey();
}
}
While running the test, the PlaceTest::testConstruct() while doing $place->getKey() it pass the test but it errors in PlaceTest::testGetKey()
How is that happen?
It seems this is the limitation of mockup builder.
http://phpunit.de/manual/3.7/en/test-doubles.html
we can't pass mockup object into another test scope.
Please leave a comment if you found another reason.

php / phpDoc - #return instance of $this class?

How do I mark a method as "returns an instance of the current class" in my phpDoc?
In the following example my IDE (Netbeans) will see that setSomething always returns a foo object.
But that's not true if I extent the object - it'll return $this, which in the second example is a bar object not a foo object.
class foo {
protected $_value = null;
/**
* Set something
*
* #param string $value the value
* #return foo
*/
public function setSomething($value) {
$this->_value = $value;
return $this;
}
}
$foo = new foo();
$out = $foo->setSomething();
So fine - setSomething returns a foo - but in the following example, it returns a bar..:
class bar extends foo {
public function someOtherMethod(){}
}
$bar = new bar();
$out = $bar->setSomething();
$out->someOtherMethod(); // <-- Here, Netbeans will think $out
// is a foo, so doesn't see this other
// method in $out's code-completion
... it'd be great to solve this as for me, code completion is a massive speed-boost.
Anyone got a clever trick, or even better, a proper way to document this with phpDoc?
Update:
As of Netbeans 7.4, the IDE supports #return self, static, and this (http://wiki.netbeans.org/NewAndNoteworthyNB74#Editor_2).
class foo {
protected $_value = null;
/**
* Set something
*
* #param string $value the value
* #return this
*/
public function setSomething($value) {
$this->_value = $value;
return $this;
}
}
class bar extends foo {
public function someOtherMethod(){}
}
Previous Answer:
We have a similar issue with a record iterator's current() method. Since the iterator is extended for many different classes, it doesn't make sense to have a #return $class associated with it. We've used #satrun77's Option 2 before, but I've used #method with some success in Netbeans.
class foo {
protected $_value = null;
/**
* Set something
*
* #param string $value the value
* #return foo
*/
public function setSomething($value) {
$this->_value = $value;
return $this;
}
}
/**
* #method bar setSomething($value)
*/
class bar extends foo {
public function someOtherMethod(){}
}
Thought I'd revisit this Q as I came across a couple of things.
Currently "return $this" isn't supported, but there is a PhpDoc request to add exactly that in v1.5:
http://pear.php.net/bugs/bug.php?id=16223
There's also a request for it in Eclipse PDT:
https://bugs.eclipse.org/bugs/show_bug.cgi?id=276082
Both are relatively old requests. I'm not going to get too excited about this being implemented any time soon, but here goes to hoping :) In the meantime, it seems there is no proper solution to this problem.
!SOLVED! - upgrade to netbeans 9.0 (stable as of July 2018?)
I have been after this for over a year and finally have an open source solution! :)
class Input extends BasicHtml
{
public function someOnlyInputFunc()
{
}
}
class Table extends BasicHtml
{
public function tableOnlyFunc()
{
}
}
abstract class BasicHtml
{
/**
*
* #param array $arrayForNow
* #return $this
*/
public function setStyle( array $arrayForNow )
{
return $this;
}
}
/////driver
$table = new Table();
$input = new Input();
$input->setStyle(array())->//now shows only Input + baseHtml functions
$table->setStyle(array())-> //now shows only Table + baseHtml functions
///note - in 8.0.2 version it shows blank obj drop downs on exact same code.
This also works with traits. As of 11/1/2018 9.0 comes as a big zip (no clean installer for windows, mac?) and you will have to search for adding the php plugings etc BUT IT DOES WORK! Took me about an hour to get it all set. I also have my old 8.x installed and running along side the new 9.0 without issue...so far (just don't run them both at same time). Plugin tip: https://www.reddit.com/r/PHP/comments/9gtaaw/how_to_run_netbeans_9_with_php_support/
Here is 3 work around:
(These are just work around. classes must not be designed and implemented to sue the behavior of an IDE)
Option 1:
make the method someOtherMethod abstract or empty method in foo class
class foo implements ifoo {
protected $_value = null;
/**
* Set something
*
* #param string $value the value
* #return ifoo
*/
public function setSomething($value) {
$this->_value = $value;
return $this;
}
// abstract method or create empty method if you want the method to be
// to be optional
abstract function someOtherMethod();
}
Option 2:
Override the method setSomething in bar class
class bar extends foo {
/**
*
* #param <type> $value
* #return bar
*/
public function setSomething($value) {
return parent::setSomething($value);
}
public function someOtherMethod(){}
}
Option 3:
Use interface
interface ifoo {
public function someOtherMethod(){}
}
class foo {
protected $_value = null;
/**
* Set something
*
* #param string $value the value
* #return ifoo
*/
public function setSomething($value) {
$this->_value = $value;
return $this;
}
}
class bar extends foo implements ifoo {
public function someOtherMethod(){}
}
phpDoc syntax allows for multiple types to be defined by separating them with a | character for the #return tag. When you extend the class foo with class bar you should write a new phpDoc tag that has the proper class for its #return.
If a function returns either foo or bar then you would use #return foo|bar.
However in your case just define #return bar for the overridden function.
Take care.

How to document class properties in PHP 5 with phpDocumentor

Take in consideration the following PHP 5 class:
class SomeClass
{
//I want to document this property...
private $foo;
function __construct()
{
}
public function SetFoo($value)
{
$this->foo = $value;
}
public function GetFoo()
{
return $this->foo;
}
}
How in phpDocumentor will I document the $foo property? I'm not even sure it need to be documented but I would like to know how if if needs to...
I know how to document SetFoo() and GetFoo(), I'm just not sure about the private property (variable?).
Thanks!
/**
* This is what the variable does. The var line contains the type stored in this variable.
* #var string
*/
private $foo;
I would generally use at least the #var tag, to indicate the type of variable this is.
For instance :
/**
* Some blah blah about what this is useful for
* #var MyClass $foo
*/
This is exactly what's done by Zend Framework, for instance ; see Zend_Layout (quoting) :
class Zend_Layout
{
/**
* Placeholder container for layout variables
* #var Zend_View_Helper_Placeholder_Container
*/
protected $_container;
/**
* Key used to store content from 'default' named response segment
* #var string
*/
protected $_contentKey = 'content';
Note : the #access tag was useful with PHP 4 (when there were no public/protected/private), but I never use it when I document code written in PHP 5 : the code, using the visibility keywords is self-documenting.
In the case you use a __get and __set magic methods you can use #property
/**
* Description for the class
* #property type $foo Description for foo
* #property type $foo Description for bar
*/
class SomeClass
{
private $foo;
protected $bar;
public function __get(){
...
}
public function __set(){
...
}
}
Links with more info:
http://www.phpdoc.org/docs/latest/for-users/phpdoc/tags/property.html
http://manual.phpdoc.org/HTMLSmartyConverter/HandS/phpDocumentor/tutorial_tags.property.pkg.html
/**
* docstring
*/
private $foo;
Important note: there should be two asterisks. Not one.

Categories