6 * Structure of an entry
9 const OF_OTHER
= "other";
10 const OF_NAMESPACE
= "ns";
11 const OF_CLASS
= "class";
12 const OF_FUNC
= "func";
18 function __construct(Entry
$entry) {
19 $this->entry
= $entry;
21 if ($entry->isRoot() ||
$entry->isNsClass()) {
22 if ($entry->isRoot()) {
23 $this->type
= self
::OF_NAMESPACE
;
24 $this->getStructureOfRoot();
25 } elseif (!strncmp($entry->getTitle(), "namespace", strlen("namespace"))) {
26 $this->type
= self
::OF_NAMESPACE
;
27 $this->getStructureOfNs();
29 $this->type
= self
::OF_CLASS
;
30 $this->getStructureOfClass();
32 } elseif ($entry->isFunction()) {
33 $this->type
= self
::OF_FUNC
;
34 $this->getStructureOfFunc();
36 $this->type
= self
::OF_OTHER
;
40 static function of(Entry
$entry) : StructureOf
{
41 return (new static($entry))->getStruct();
44 function getStruct() : StructureOf
{
49 $this->struct
->format();
52 private function getStructureOfFunc() : StructureOfFunc
{
53 return $this->struct
= new StructureOfFunc([
54 "ns" => $this->entry
->getParent()->getNsName(),
55 "name" => $this->entry
->getEntryName(),
56 "desc" => $this->entry
->getFullDescription(),
57 "returns" => $this->getReturns(),
58 "params" => $this->getParams(),
59 "throws" => $this->getThrows()
63 private function getStructureOfClass() : StructureOfClass
{
64 return $this->struct
= new StructureOfClass([
65 "ns" => $this->entry
->getParent()->getNsName(),
66 "name" => $this->prepareClassName(),
67 "desc" => $this->entry
->getFullDescription(),
68 "consts" => $this->getConstants(),
69 "props" => $this->getProperties(),
70 "funcs" => $this->getFunctions(),
71 "classes" => $this->getClasses(),
75 private function getStructureOfNs() : StructureOfNs
{
76 return $this->struct
= new StructureOfNs([
77 "name" => $this->entry
->getNsName(),
78 "desc" => $this->entry
->getFullDescription(),
79 "consts" => $this->getConstants(),
80 "classes" => $this->getClasses(),
84 private function getStructureOfRoot() : StructureOfRoot
{
85 return $this->struct
= new StructureOfRoot([
86 "name" => $this->entry
->getName(),
87 "desc" => $this->entry
->getFile()->readIntro(),
88 "consts" => $this->getConstants(),
89 "classes" => $this->getClasses()
93 private function getSection(string $section) : string {
94 return $this->entry
->getFile()->readSection($section);
97 private function prepareClassName() {
98 return preg_replace_callback_array([
99 '/(?P<type>class|interface|trait)\s+([\\\\\w]+\\\)?(?P<name>\w+)\s*/' => function($match) {
100 return $match["type"] . " " . $match["name"] . " ";
102 '/(?P<op>extends|implements)\s+(?P<names>[\\w]+(?:(?:,\s*[\\\\\w]+)*))/' => function ($match) {
103 return $match["op"] . " " . preg_replace('/\b(?<!\\\)(\w)/', '\\\\\\1', $match["names"]);
105 ], $this->entry
->getTitle());
108 private function splitList(string $pattern, string $text) : array {
110 if (strlen($text) && !preg_match("/^None/", $text)) {
111 if (preg_match_all($pattern, $text, $matches, PREG_SET_ORDER
)) {
118 private function getConstantValue(string $name) {
119 $ns = $this->entry
->getNsName();
120 if (defined("\\$ns::$name")) {
121 return constant("\\$ns::$name");
123 if (defined("\\$ns\\$name")) {
124 return constant("\\$ns\\$name");
129 private function getConstants() : array {
133 (?:\s*=\s*(?P<value>.+))?
134 (?P<desc>(?:\s*\n\s*[^\*\n#].*)*)
138 $consts = $this->splitList($pattern, $this->getSection("Constants"));
139 foreach ($consts as $const) {
140 if (!isset($const["value"]) ||
!strlen($const["value"])) {
141 $const["value"] = $this->getConstantValue($const["name"]);
143 $structs[$const["name"]] = new StructureOfConst($const);
148 private function getProperties() : array {
151 (?P<modifiers>\w+\s+)*
152 (?P<type>[\\\\\w]+)\s+
154 (?:\s*=\s*(?P<defval>.+))?
155 (?P<desc>(?:\s*\n\s*[^\*].*)*)
159 $props = $this->splitList($pattern, $this->getSection("Properties"));
160 foreach ($props as $prop) {
161 $structs[$prop["name"]] = new StructureOfVar($prop);
166 private function getFunctions() : array {
168 foreach ($this->entry
as $sub) {
169 if ($sub->isFunction()) {
170 $structs[$sub->getEntryName()] = static::of($sub);
176 private function getClasses() : array {
178 foreach ($this->entry
as $sub) {
179 if ($sub->isNsClass()) {
180 $structs[$sub->getEntryName()] = static::of($sub);
186 private function getParams() : array {
189 (?P<modifiers>\w+\s+)*
190 (?P<type>[\\\\\w_]+)\s+
191 (?P<ref>&)?(?P<name>\$[\w_]+)
192 (?:\s*=\s*(?P<defval>.+))?
193 (?P<desc>(?:\s*[^*]*\n(?!\n)\s*[^\*].*)*)
197 $params = $this->splitList($pattern, $this->getSection("Params"));
198 foreach ($params as $param) {
199 $structs[$param["name"]] = new StructureOfVar($param);
204 private function getReturns() : array {
209 (?P<desc>(?:.|\n(?!\s*\*))*)
212 $returns = $this->splitList($pattern, $this->getSection("Returns"));
214 foreach ($returns as list(, $type, $desc)) {
215 $retvals[] = [$type, $desc];
218 return [implode("|", array_unique(array_column($returns, "type"))), $retdesc];
221 private function getThrows() : array {
224 (?P<exception>[\\\\\w]+)\s*
227 $throws = $this->splitList($pattern, $this->getSection("Throws"));
228 return array_column($throws, "exception");
232 abstract class StructureOf
{
233 function __construct(array $props = []) {
234 foreach ($props as $key => $val) {
238 if (!property_exists(static::class, $key)) {
239 throw new \
UnexpectedValueException(
240 sprintf("Property %s::\$%s does not exist", static::class, $key)
243 if ($key === "desc" ||
$key === "modifiers" ||
$key === "defval") {
250 // abstract function format();
252 function formatDesc($level, array $tags = []) {
253 $indent = str_repeat("\t", $level);
254 $desc = trim($this->desc
);
255 if (false !== stristr($desc, "deprecated in")) {
256 $tags[] = "deprecated";
259 $desc .= "\n\n@" . implode("\n@", $tags);
261 $desc = preg_replace('/[\t ]*\n/',"\n$indent * ", $desc);
262 printf("%s/**\n%s * %s\n%s */\n", $indent, $indent, $desc, $indent);
265 function saneTypes(array $types) {
267 foreach ($types as $type) {
268 if (strlen($s = $this->saneType($type, false))) {
275 function saneType($type, $strict = true) {
276 switch (strtolower($type)) {
297 return ($type{0} === "\\" ?
"":"\\") . $type;
303 class StructureOfRoot
extends StructureOf
{
310 $this->formatDesc(0);
312 foreach ($this->consts
as $const) {
317 printf("namespace %s;\nuse %s;\n", $this->name
, $this->name
);
318 StructureOfNs
::$last = $this->name
;
320 foreach ($this->getClasses() as $class) {
325 function getClasses() {
326 yield from
$this->classes
;
327 foreach ($this->classes
as $class) {
328 yield from
$class->getClasses();
332 class StructureOfNs
extends StructureOfRoot
{
338 print $this->formatDesc(0);
340 if (strlen($this->name
) && $this->name
!== StructureOfNs
::$last) {
341 StructureOfNs
::$last = $this->name
;
342 printf("namespace %s;\n", $this->name
);
344 foreach ($this->consts
as $const) {
351 class StructureOfClass
extends StructureOfNs
357 if ($this->ns
!== StructureOfNs
::$last) {
358 printf("namespace %s;\n", $this->ns
);
359 StructureOfNs
::$last = $this->ns
;
362 print $this->formatDesc(0);
363 printf("%s {\n", $this->name
);
365 foreach ($this->consts
as $const) {
370 foreach ($this->props
as $prop) {
371 $prop->formatAsProp(1);
375 foreach ($this->funcs
as $func) {
377 if (strncmp($this->name
, "interface", strlen("interface"))) {
388 class StructureOfFunc
extends StructureOf
{
397 function omitParamTypes() {
398 switch ($this->name
) {
412 function format(int $level) {
414 foreach ($this->params
as $param) {
415 $type = $this->saneType($param->type
, false);
416 $tags[] = "param {$type} {$param->name} {$param->desc}";
418 foreach ($this->throws
as $throws) {
419 $tags[] = "throws " . $this->saneType($throws);
421 if ($this->name
!== "__construct" && $this->returns
) {
423 if (count($this->returns
) > 1) {
424 $type = implode("|", $this->saneTypes(array_column($this->returns
, 0)));
426 foreach ($this->returns
as list($typ, $ret)) {
428 $desc .= "\n\t\t or ";
430 $desc .= $this->saneType($typ, false) . " " . $ret;
433 $type = $this->saneType($this->returns
[0][0], false);
434 $desc = $this->returns
[0][1];
436 $tags[] = "return $type $desc";
438 $this->formatDesc(1, $tags);
439 printf("\tfunction %s(", $this->name
);
441 $omit = $this->omitParamTypes();
442 foreach ($this->params
as $param) {
444 $param->formatAsParam($level, !$omit);
451 class StructureOfConst
extends StructureOf
{
456 function format(int $level) {
457 $indent = str_repeat("\t", $level);
458 $this->formatDesc($level);
459 printf("%sconst %s = ", $indent, $this->name
);
460 var_export($this->value
);
464 class StructureOfVar
extends StructureOf
{
472 function formatDefval() {
473 if (strlen($this->defval
)) {
474 if (false && defined($this->defval
)) {
476 var_export(constant($this->defval
));
477 } else if (strlen($this->defval
)) {
478 if (false !== strchr($this->defval
, "\\") && $this->defval
{0} != "\\") {
479 $this->defval
= "\\" . $this->defval
;
481 printf(" = %s", $this->defval
);
483 } elseif ($this->modifiers
) {
484 if (stristr($this->modifiers
, "optional") !== false) {
489 function formatAsProp($level) {
490 $indent = str_repeat("\t", $level);
491 $this->formatDesc($level,
492 preg_split('/\s+/', $this->modifiers
, -1, PREG_SPLIT_NO_EMPTY
)
493 +
[-1 => "var " . $this->saneType($this->type
)]
495 printf("%s%s %s", $indent, $this->modifiers
, $this->name
);
496 $this->formatDefval();
499 function formatAsParam($level, $with_type = true) {
500 if ($with_type && strlen($type = $this->saneType($this->type
))) {
501 printf("%s ", $type);
503 printf("%s%s", $this->ref
, $this->name
);
504 $this->formatDefval();