Skip to content

Commit ac88751

Browse files
committed
Add initial implementation of ReflectionProperty class
1 parent 1560d66 commit ac88751

File tree

1 file changed

+159
-0
lines changed

1 file changed

+159
-0
lines changed

src/Reflection/ReflectionProperty.php

+159
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
<?php
2+
/**
3+
* Z-Engine framework
4+
*
5+
* @copyright Copyright 2020, Lisachenko Alexander <[email protected]>
6+
*
7+
* This source file is subject to the license that is bundled
8+
* with this source code in the file LICENSE.
9+
*
10+
*/
11+
declare(strict_types=1);
12+
13+
namespace ZEngine\Reflection;
14+
15+
use FFI\CData;
16+
use ReflectionProperty as NativeReflectionProperty;
17+
use ZEngine\Core;
18+
use ZEngine\Type\HashTable;
19+
use ZEngine\Type\StringEntry;
20+
21+
/**
22+
* Class ReflectionProperty
23+
*
24+
* typedef struct _zend_property_info {
25+
* uint32_t offset; // property offset for object properties or property index for static properties
26+
* uint32_t flags;
27+
* zend_string *name;
28+
* zend_string *doc_comment;
29+
* zend_class_entry *ce;
30+
* zend_type type;
31+
* } zend_property_info;
32+
*/
33+
class ReflectionProperty extends NativeReflectionProperty
34+
{
35+
private CData $pointer;
36+
37+
public function __construct(string $className, string $propertyName)
38+
{
39+
parent::__construct($className, $propertyName);
40+
41+
$normalizedName = strtolower($className);
42+
$classEntryValue = Core::$executor->classTable->find($normalizedName);
43+
if ($classEntryValue === null) {
44+
throw new \ReflectionException("Class {$className} should be in the engine.");
45+
}
46+
$classEntry = $classEntryValue->getRawClass();
47+
$propertiesTable = new HashTable(Core::addr($classEntry->properties_info));
48+
49+
$propertyEntry = $propertiesTable->find(strtolower($propertyName));
50+
if ($propertyEntry === null) {
51+
throw new \ReflectionException("Property {$propertyName} was not found in the class.");
52+
}
53+
$propertyPointer = $propertyEntry->getRawPointer();
54+
$this->pointer = Core::cast('zend_property_info *', $propertyPointer);
55+
}
56+
57+
/**
58+
* Creates a reflection from the zend_property_info structure
59+
*
60+
* @param CData $propertyEntry Pointer to the structure
61+
*/
62+
public static function fromCData(CData $propertyEntry): ReflectionProperty
63+
{
64+
/** @var ReflectionProperty $reflectionProperty */
65+
$reflectionProperty = (new ReflectionClass(static::class))->newInstanceWithoutConstructor();
66+
$propertyName = StringEntry::fromCData($propertyEntry->name);
67+
call_user_func(
68+
[$reflectionProperty, 'parent::__construct'],
69+
$propertyName->getStringValue()
70+
);
71+
$reflectionProperty->pointer = $propertyEntry;
72+
73+
return $reflectionProperty;
74+
}
75+
76+
/**
77+
* Returns an offset of this property
78+
*/
79+
public function getOffset(): int
80+
{
81+
return $this->pointer->offset;
82+
}
83+
84+
/**
85+
* Declares property as public
86+
*/
87+
public function setPublic(): void
88+
{
89+
$this->pointer->flags &= (~Core::ZEND_ACC_PPP_MASK);
90+
$this->pointer->flags |= Core::ZEND_ACC_PUBLIC;
91+
}
92+
93+
/**
94+
* Declares property as protected
95+
*/
96+
public function setProtected(): void
97+
{
98+
$this->pointer->flags &= (~Core::ZEND_ACC_PPP_MASK);
99+
$this->pointer->flags |= Core::ZEND_ACC_PROTECTED;
100+
}
101+
102+
/**
103+
* Declares property as private
104+
*/
105+
public function setPrivate(): void
106+
{
107+
$this->pointer->flags &= (~Core::ZEND_ACC_PPP_MASK);
108+
$this->pointer->flags |= Core::ZEND_ACC_PRIVATE;
109+
}
110+
111+
/**
112+
* Declares property as static/non-static
113+
*/
114+
public function setStatic(bool $isStatic = true): void
115+
{
116+
if ($isStatic) {
117+
$this->pointer->flags |= Core::ZEND_ACC_STATIC;
118+
} else {
119+
$this->pointer->flags &= (~Core::ZEND_ACC_STATIC);
120+
}
121+
}
122+
123+
/**
124+
* Gets the declaring class
125+
*/
126+
public function getDeclaringClass(): ReflectionClass
127+
{
128+
return ReflectionClass::fromCData($this->pointer->ce);
129+
}
130+
131+
/**
132+
* Changes the declaring class name for this property
133+
*
134+
* @param string $className New class name for this property
135+
*/
136+
public function setDeclaringClass(string $className): void
137+
{
138+
$lcName = strtolower($className);
139+
140+
$classEntryValue = Core::$executor->classTable->find($lcName);
141+
if ($classEntryValue === null) {
142+
throw new \ReflectionException("Class {$className} was not found");
143+
}
144+
$this->pointer->ce = $classEntryValue->getRawClass();
145+
}
146+
147+
/**
148+
* Returns a user-friendly representation of internal structure to prevent segfault
149+
*/
150+
public function __debugInfo(): array
151+
{
152+
return [
153+
'name' => $this->getName(),
154+
'offset' => $this->getOffset(),
155+
'type' => $this->getType(),
156+
'class' => $this->getDeclaringClass()->getName()
157+
];
158+
}
159+
}

0 commit comments

Comments
 (0)