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