1: <?php
2:
3: declare(strict_types=1);
4:
5: namespace PHPStan\BetterReflection\Reflection\Adapter;
6:
7: use ArgumentCountError;
8: use OutOfBoundsException;
9: use PhpParser\Node\Expr;
10: use ReflectionException as CoreReflectionException;
11: use ReflectionProperty as CoreReflectionProperty;
12: use ReturnTypeWillChange;
13: use PHPStan\BetterReflection\Reflection\Exception\NoObjectProvided;
14: use PHPStan\BetterReflection\Reflection\Exception\NotAnObject;
15: use PHPStan\BetterReflection\Reflection\ReflectionAttribute as BetterReflectionAttribute;
16: use PHPStan\BetterReflection\Reflection\ReflectionProperty as BetterReflectionProperty;
17: use Throwable;
18: use TypeError;
19: use ValueError;
20:
21: use function array_map;
22: use function gettype;
23: use function sprintf;
24:
25: /** @psalm-suppress PropertyNotSetInConstructor */
26: final class ReflectionProperty extends CoreReflectionProperty
27: {
28: public const IS_READONLY = 128;
29: /**
30: * @var BetterReflectionProperty
31: */
32: private $betterReflectionProperty;
33:
34: public function __construct(BetterReflectionProperty $betterReflectionProperty)
35: {
36: $this->betterReflectionProperty = $betterReflectionProperty;
37: unset($this->name);
38: unset($this->class);
39: }
40:
41: public function __toString(): string
42: {
43: return $this->betterReflectionProperty->__toString();
44: }
45:
46: public function getName(): string
47: {
48: return $this->betterReflectionProperty->getName();
49: }
50:
51: /**
52: * {@inheritDoc}
53: * @return mixed
54: */
55: #[ReturnTypeWillChange]
56: public function getValue($object = null)
57: {
58: try {
59: return $this->betterReflectionProperty->getValue($object);
60: } catch (NoObjectProvided $exception) {
61: return null;
62: } catch (Throwable $e) {
63: throw new CoreReflectionException($e->getMessage(), 0, $e);
64: }
65: }
66:
67: /** @psalm-suppress MethodSignatureMismatch
68: * @param mixed $objectOrValue
69: * @param mixed $value */
70: public function setValue($objectOrValue, $value = null): void
71: {
72: try {
73: $this->betterReflectionProperty->setValue($objectOrValue, $value);
74: } catch (NoObjectProvided $exception) {
75: throw new ArgumentCountError('ReflectionProperty::setValue() expects exactly 2 arguments, 1 given');
76: } catch (NotAnObject $exception) {
77: throw new TypeError(sprintf('ReflectionProperty::setValue(): Argument #1 ($objectOrValue) must be of type object, %s given', gettype($objectOrValue)));
78: } catch (Throwable $e) {
79: throw new CoreReflectionException($e->getMessage(), 0, $e);
80: }
81: }
82:
83: public function hasType(): bool
84: {
85: return $this->betterReflectionProperty->hasType();
86: }
87:
88: /**
89: * @return ReflectionUnionType|ReflectionNamedType|ReflectionIntersectionType|null
90: */
91: public function getType(): ?\ReflectionType
92: {
93: return ReflectionType::fromTypeOrNull($this->betterReflectionProperty->getType());
94: }
95:
96: public function isPublic(): bool
97: {
98: return $this->betterReflectionProperty->isPublic();
99: }
100:
101: public function isPrivate(): bool
102: {
103: return $this->betterReflectionProperty->isPrivate();
104: }
105:
106: public function isProtected(): bool
107: {
108: return $this->betterReflectionProperty->isProtected();
109: }
110:
111: public function isStatic(): bool
112: {
113: return $this->betterReflectionProperty->isStatic();
114: }
115:
116: public function isDefault(): bool
117: {
118: return $this->betterReflectionProperty->isDefault();
119: }
120:
121: public function getModifiers(): int
122: {
123: return $this->betterReflectionProperty->getModifiers();
124: }
125:
126: public function getDeclaringClass(): ReflectionClass
127: {
128: return new ReflectionClass($this->betterReflectionProperty->getImplementingClass());
129: }
130:
131: /**
132: * {@inheritDoc}
133: */
134: #[ReturnTypeWillChange]
135: public function getDocComment()
136: {
137: return $this->betterReflectionProperty->getDocComment() ?? false;
138: }
139:
140: /**
141: * {@inheritDoc}
142: * @codeCoverageIgnore
143: * @infection-ignore-all
144: */
145: public function setAccessible($accessible): void
146: {
147: }
148:
149: public function hasDefaultValue(): bool
150: {
151: return $this->betterReflectionProperty->hasDefaultValue();
152: }
153:
154: /**
155: * @deprecated Use getDefaultValueExpression()
156: * @return mixed
157: */
158: #[ReturnTypeWillChange]
159: public function getDefaultValue()
160: {
161: return $this->betterReflectionProperty->getDefaultValue();
162: }
163:
164: /**
165: * @deprecated Use getDefaultValueExpression()
166: */
167: public function getDefaultValueExpr(): Expr
168: {
169: return $this->betterReflectionProperty->getDefaultValueExpression();
170: }
171:
172: public function getDefaultValueExpression(): Expr
173: {
174: return $this->betterReflectionProperty->getDefaultValueExpression();
175: }
176:
177: /**
178: * {@inheritDoc}
179: */
180: #[ReturnTypeWillChange]
181: public function isInitialized($object = null)
182: {
183: try {
184: return $this->betterReflectionProperty->isInitialized($object);
185: } catch (Throwable $e) {
186: throw new CoreReflectionException($e->getMessage(), 0, $e);
187: }
188: }
189:
190: public function isPromoted(): bool
191: {
192: return $this->betterReflectionProperty->isPromoted();
193: }
194:
195: /**
196: * @param class-string|null $name
197: *
198: * @return list<ReflectionAttribute|FakeReflectionAttribute>
199: */
200: public function getAttributes(?string $name = null, int $flags = 0): array
201: {
202: if ($flags !== 0 && $flags !== ReflectionAttribute::IS_INSTANCEOF) {
203: throw new ValueError('Argument #2 ($flags) must be a valid attribute filter flag');
204: }
205:
206: if ($name !== null && $flags !== 0) {
207: $attributes = $this->betterReflectionProperty->getAttributesByInstance($name);
208: } elseif ($name !== null) {
209: $attributes = $this->betterReflectionProperty->getAttributesByName($name);
210: } else {
211: $attributes = $this->betterReflectionProperty->getAttributes();
212: }
213:
214: return array_map(static function (BetterReflectionAttribute $betterReflectionAttribute) {
215: return ReflectionAttributeFactory::create($betterReflectionAttribute);
216: }, $attributes);
217: }
218:
219: public function isReadOnly(): bool
220: {
221: return $this->betterReflectionProperty->isReadOnly();
222: }
223:
224: /**
225: * @return mixed
226: */
227: public function __get(string $name)
228: {
229: if ($name === 'name') {
230: return $this->betterReflectionProperty->getName();
231: }
232:
233: if ($name === 'class') {
234: return $this->betterReflectionProperty->getImplementingClass()->getName();
235: }
236:
237: throw new OutOfBoundsException(sprintf('Property %s::$%s does not exist.', self::class, $name));
238: }
239:
240: public function getBetterReflection(): BetterReflectionProperty
241: {
242: return $this->betterReflectionProperty;
243: }
244: }
245: