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