Completed
Push — master ( 748172...28d82c )
by Mike
06:47
created

PropertyDescriptor::isReadOnly()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 4
ccs 0
cts 0
cp 0
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * This file is part of phpDocumentor.
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 *
11
 * @link https://phpdoc.org
12
 */
13
14
namespace phpDocumentor\Descriptor;
15
16
use phpDocumentor\Descriptor\Tag\VarDescriptor;
17
use phpDocumentor\Reflection\Fqsen;
18
use phpDocumentor\Reflection\Type;
19
20
/**
21
 * Descriptor representing a property.
22
 *
23
 * @api
24
 * @package phpDocumentor\AST
25
 */
26
class PropertyDescriptor extends DescriptorAbstract implements
27
    Interfaces\PropertyInterface,
28
    Interfaces\VisibilityInterface
29
{
30
    /** @var ClassDescriptor|TraitDescriptor|null $parent */
31
    protected $parent;
32
33
    /** @var Type|null $type */
34
    protected $type;
35
36
    /** @var string $default */
37
    protected $default;
38
39
    /** @var bool $static */
40
    protected $static = false;
41
42
    /** @var string $visibility */
43
    protected $visibility = 'public';
44
45
    /** @var bool */
46
    private $readOnly = false;
47
48 2
    /** @var bool */
49
    private $writeOnly = false;
50 2
51
    /**
52 2
     * @param ClassDescriptor|TraitDescriptor $parent
53 2
     */
54
    public function setParent(DescriptorAbstract $parent) : void
55 2
    {
56
        $this->parent = $parent;
0 ignored issues
show
Documentation Bug introduced by
It seems like $parent of type object<phpDocumentor\Des...tor\DescriptorAbstract> is incompatible with the declared type object<phpDocumentor\Des...r\TraitDescriptor>|null of property $parent.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
57
58
        $this->setFullyQualifiedStructuralElementName(
59
            new Fqsen($parent->getFullyQualifiedStructuralElementName() . '::$' . $this->getName())
60 1
        );
61
    }
62 1
63
    /**
64
     * @return ClassDescriptor|TraitDescriptor|null
65 1
     */
66
    public function getParent() : ?DescriptorAbstract
67 1
    {
68 1
        return $this->parent;
69
    }
70 1
71
    public function setDefault(?string $default) : void
72 1
    {
73
        $this->default = $default;
74
    }
75 1
76
    public function getDefault() : ?string
77 1
    {
78 1
        return $this->default;
79
    }
80 1
81
    public function setStatic(bool $static) : void
82 1
    {
83
        $this->static = $static;
84
    }
85 1
86
    public function isStatic() : bool
87 1
    {
88 1
        return $this->static;
89
    }
90
91
    public function setType(Type $type) : void
92
    {
93 1
        $this->type = $type;
94
    }
95 1
96 1
    /**
97
     * @return list<string>
0 ignored issues
show
Documentation introduced by
The doc-type list<string> could not be parsed: Expected "|" or "end of type", but got "<" at position 4. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
98
     */
99 1
    public function getTypes() : array
100
    {
101
        if ($this->getType() instanceof Type) {
102 2
            return [(string) $this->getType()];
103
        }
104 2
105
        return [];
106 2
    }
107 2
108 1
    public function getType() : ?Type
109
    {
110
        if ($this->type === null) {
111
            /** @var VarDescriptor|bool $var */
112 1
            $var = $this->getVar()->getIterator()->current();
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Traversable as the method current() does only exist in the following implementations of said interface: APCUIterator, AppendIterator, ArrayIterator, CachingIterator, CallbackFilterIterator, DirectoryIterator, Doctrine\RST\Parser\Lines, EmptyIterator, File_Iterator, FilesystemIterator, FilterIterator, Generator, GlobIterator, HttpMessage, HttpRequestPool, Imagick, ImagickPixelIterator, InfiniteIterator, IteratorIterator, LimitIterator, MongoCommandCursor, MongoCursor, MongoGridFSCursor, MultipleIterator, NoRewindIterator, ParentIterator, Phar, PharData, RecursiveArrayIterator, RecursiveCachingIterator, RecursiveCallbackFilterIterator, RecursiveDirectoryIterator, RecursiveFilterIterator, RecursiveIteratorIterator, RecursiveRegexIterator, RecursiveTreeIterator, RegexIterator, SQLiteResult, SimpleXMLIterator, SplDoublyLinkedList, SplFileObject, SplFixedArray, SplHeap, SplMaxHeap, SplMinHeap, SplObjectStorage, SplPriorityQueue, SplQueue, SplStack, SplTempFileObject, Symfony\Component\Finder...or\CustomFilterIterator, Symfony\Component\Finder...DateRangeFilterIterator, Symfony\Component\Finder...epthRangeFilterIterator, Symfony\Component\Finder...DirectoryFilterIterator, Symfony\Component\Finder...\FileTypeFilterIterator, Symfony\Component\Finder...lecontentFilterIterator, Symfony\Component\Finder...\FilenameFilterIterator, Symfony\Component\Finder...tiplePcreFilterIterator, Symfony\Component\Finder...ator\PathFilterIterator, Symfony\Component\Finder...ursiveDirectoryIterator, Symfony\Component\Finder...SizeRangeFilterIterator, Twig\Util\TemplateDirIterator, Twig_Util_TemplateDirIterator, org\bovigo\vfs\vfsStreamContainerIterator, phpDocumentor\Compiler\Compiler, phpDocumentor\Reflection...y\ClassConstantIterator, phpDocumentor\Reflection...\GlobalConstantIterator, phpDocumentor\Reflection...actory\PropertyIterator.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
113
            if ($var instanceof VarDescriptor) {
114
                return $var->getType();
115 1
            }
116
        }
117 1
118 1
        return $this->type;
119
    }
120 1
121
    public function setVisibility(string $visibility) : void
122 1
    {
123
        $this->visibility = $visibility;
124
    }
125
126
    public function getVisibility() : string
127
    {
128 2
        return $this->visibility;
129
    }
130
131 2
    /**
132 2
     * @return Collection<VarDescriptor>
0 ignored issues
show
Documentation introduced by
The doc-type Collection<VarDescriptor> could not be parsed: Expected "|" or "end of type", but got "<" at position 10. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
133 1
     */
134
    public function getVar() : Collection
135
    {
136 2
        /** @var Collection<VarDescriptor> $var */
0 ignored issues
show
Documentation introduced by
The doc-type Collection<VarDescriptor> could not be parsed: Expected "|" or "end of type", but got "<" at position 10. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
137 2
        $var = $this->getTags()->fetch('var', new Collection());
138 1
        if ($var->count() !== 0) {
139
            return $var;
140
        }
141 1
142
        $inheritedElement = $this->getInheritedElement();
143
        if ($inheritedElement) {
144
            return $inheritedElement->getVar();
145
        }
146
147 1
        return new Collection();
148
    }
149 1
150
    /**
151
     * Returns the file associated with the parent class or trait.
152
     */
153
    public function getFile() : FileDescriptor
154
    {
155 2
        return $this->getParent()->getFile();
156
    }
157
158 2
    /**
159
     * Returns the property from which this one should inherit, if any.
160 2
     */
161 1
    public function getInheritedElement() : ?PropertyDescriptor
162 2
    {
163
        /** @var ClassDescriptor|InterfaceDescriptor|null $associatedClass */
164
        $associatedClass = $this->getParent();
165
166 1
        if (($associatedClass instanceof ClassDescriptor || $associatedClass instanceof InterfaceDescriptor)
167
            && ($associatedClass->getParent() instanceof ClassDescriptor
168 1
                || $associatedClass->getParent() instanceof InterfaceDescriptor
169
            )
170
        ) {
171 1
            /** @var ClassDescriptor|InterfaceDescriptor $parentClass */
172
            $parentClass = $associatedClass->getParent();
173
174
            return $parentClass->getProperties()->fetch($this->getName());
0 ignored issues
show
Bug introduced by
The method getProperties does only exist in phpDocumentor\Descriptor\ClassDescriptor, but not in phpDocumentor\Descriptor\InterfaceDescriptor.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
175
        }
176
177
        return null;
178
    }
179
180
    public function setReadOnly(bool $value): void
181
    {
182
        $this->readOnly = $value;
183
    }
184
185
    public function isReadOnly() : bool
186
    {
187
        return $this->readOnly;
188
    }
189
190
    public function setWriteOnly(bool $value): void
191
    {
192
        $this->writeOnly = $value;
193
    }
194
195
    public function isWriteOnly() : bool
196
    {
197
        return $this->writeOnly;
198
    }
199
}
200